Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_switchdev.c
index 7b654c5..7186c48 100644 (file)
@@ -254,12 +254,40 @@ int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 fid,
        return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set, set);
 }
 
+static int mlxsw_sp_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                     bool set)
+{
+       u16 vid;
+       int err;
+
+       if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+               vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+
+               return __mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, vid,
+                                                       set);
+       }
+
+       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
+               err = __mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, vid,
+                                                      set);
+               if (err)
+                       goto err_port_vid_learning_set;
+       }
+
+       return 0;
+
+err_port_vid_learning_set:
+       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
+               __mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, vid, !set);
+       return err;
+}
+
 static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
                                           struct switchdev_trans *trans,
                                           unsigned long brport_flags)
 {
+       unsigned long learning = mlxsw_sp_port->learning ? BR_LEARNING : 0;
        unsigned long uc_flood = mlxsw_sp_port->uc_flood ? BR_FLOOD : 0;
-       bool set;
        int err;
 
        if (!mlxsw_sp_port->bridged)
@@ -269,17 +297,30 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
                return 0;
 
        if ((uc_flood ^ brport_flags) & BR_FLOOD) {
-               set = mlxsw_sp_port->uc_flood ? false : true;
-               err = mlxsw_sp_port_uc_flood_set(mlxsw_sp_port, set);
+               err = mlxsw_sp_port_uc_flood_set(mlxsw_sp_port,
+                                                !mlxsw_sp_port->uc_flood);
                if (err)
                        return err;
        }
 
+       if ((learning ^ brport_flags) & BR_LEARNING) {
+               err = mlxsw_sp_port_learning_set(mlxsw_sp_port,
+                                                !mlxsw_sp_port->learning);
+               if (err)
+                       goto err_port_learning_set;
+       }
+
        mlxsw_sp_port->uc_flood = brport_flags & BR_FLOOD ? 1 : 0;
        mlxsw_sp_port->learning = brport_flags & BR_LEARNING ? 1 : 0;
        mlxsw_sp_port->learning_sync = brport_flags & BR_LEARNING_SYNC ? 1 : 0;
 
        return 0;
+
+err_port_learning_set:
+       if ((uc_flood ^ brport_flags) & BR_FLOOD)
+               mlxsw_sp_port_uc_flood_set(mlxsw_sp_port,
+                                          mlxsw_sp_port->uc_flood);
+       return err;
 }
 
 static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time)
@@ -631,6 +672,27 @@ static int __mlxsw_sp_port_vlans_set(struct mlxsw_sp_port *mlxsw_sp_port,
        return 0;
 }
 
+static int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                         u16 vid_begin, u16 vid_end,
+                                         bool learn_enable)
+{
+       u16 vid, vid_e;
+       int err;
+
+       for (vid = vid_begin; vid <= vid_end;
+            vid += MLXSW_REG_SPVMLR_REC_MAX_COUNT) {
+               vid_e = min((u16) (vid + MLXSW_REG_SPVMLR_REC_MAX_COUNT - 1),
+                           vid_end);
+
+               err = __mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid,
+                                                      vid_e, learn_enable);
+               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)
@@ -671,6 +733,14 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
                }
        }
 
+       err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid_begin, vid_end,
+                                            mlxsw_sp_port->learning);
+       if (err) {
+               netdev_err(dev, "Failed to set learning for VIDs %d-%d\n",
+                          vid_begin, vid_end);
+               goto err_port_vid_learning_set;
+       }
+
        /* Changing activity bits only if HW operation succeded */
        for (vid = vid_begin; vid <= vid_end; vid++) {
                set_bit(vid, mlxsw_sp_port->active_vlans);
@@ -693,6 +763,9 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
 err_port_stp_state_set:
        for (vid = vid_begin; vid <= vid_end; vid++)
                clear_bit(vid, mlxsw_sp_port->active_vlans);
+       mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid_begin, vid_end,
+                                      false);
+err_port_vid_learning_set:
        if (old_pvid != mlxsw_sp_port->pvid)
                mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid);
 err_port_pvid_set:
@@ -997,29 +1070,20 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
 static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
                                     u16 vid_begin, u16 vid_end)
 {
-       struct net_device *dev = mlxsw_sp_port->dev;
        u16 vid, pvid;
-       int err;
 
        if (!mlxsw_sp_port->bridged)
                return -EINVAL;
 
-       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;
-       }
+       mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid_begin, vid_end,
+                                      false);
 
        pvid = mlxsw_sp_port->pvid;
-       if (pvid >= vid_begin && pvid <= vid_end) {
-               err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, 0);
-               if (err) {
-                       netdev_err(dev, "Unable to del PVID %d\n", pvid);
-                       return err;
-               }
-       }
+       if (pvid >= vid_begin && pvid <= vid_end)
+               mlxsw_sp_port_pvid_set(mlxsw_sp_port, 0);
+
+       __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, false,
+                                 false);
 
        mlxsw_sp_port_fid_leave(mlxsw_sp_port, vid_begin, vid_end);
 
@@ -1362,8 +1426,6 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
                vid = fid;
        }
 
-       adding = adding && mlxsw_sp_port->learning;
-
 do_fdb_op:
        err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid,
                                      adding, true);
@@ -1425,8 +1487,6 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
                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);
@@ -1492,20 +1552,18 @@ static void mlxsw_sp_fdb_notify_work(struct work_struct *work)
        mlxsw_sp = container_of(work, struct mlxsw_sp, fdb_notify.dw.work);
 
        rtnl_lock();
-       do {
-               mlxsw_reg_sfn_pack(sfn_pl);
-               err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
-               if (err) {
-                       dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n");
-                       break;
-               }
-               num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl);
-               for (i = 0; i < num_rec; i++)
-                       mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
+       mlxsw_reg_sfn_pack(sfn_pl);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
+       if (err) {
+               dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n");
+               goto out;
+       }
+       num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl);
+       for (i = 0; i < num_rec; i++)
+               mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
 
-       } while (num_rec);
+out:
        rtnl_unlock();
-
        kfree(sfn_pl);
        mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
 }