net/mlx5: Remove counter from idr after removing it from list
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / fs_counters.c
index 09206c4acd9a890d384b156d74f729910e4a43aa..32accd6b041b3b3d16402bcb03204be730794c3c 100644 (file)
@@ -99,6 +99,18 @@ static void mlx5_fc_stats_insert(struct mlx5_core_dev *dev,
        list_add_tail(&counter->list, next);
 }
 
+static void mlx5_fc_stats_remove(struct mlx5_core_dev *dev,
+                                struct mlx5_fc *counter)
+{
+       struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
+
+       list_del(&counter->list);
+
+       spin_lock(&fc_stats->counters_idr_lock);
+       WARN_ON(!idr_remove(&fc_stats->counters_idr, counter->id));
+       spin_unlock(&fc_stats->counters_idr_lock);
+}
+
 /* The function returns the last counter that was queried so the caller
  * function can continue calling it till all counters are queried.
  */
@@ -179,20 +191,23 @@ static void mlx5_fc_stats_work(struct work_struct *work)
        struct mlx5_core_dev *dev = container_of(work, struct mlx5_core_dev,
                                                 priv.fc_stats.work.work);
        struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
-       struct llist_node *tmplist = llist_del_all(&fc_stats->addlist);
+       /* Take dellist first to ensure that counters cannot be deleted before
+        * they are inserted.
+        */
+       struct llist_node *dellist = llist_del_all(&fc_stats->dellist);
+       struct llist_node *addlist = llist_del_all(&fc_stats->addlist);
        struct mlx5_fc *counter = NULL, *last = NULL, *tmp;
        unsigned long now = jiffies;
 
-       if (tmplist || !list_empty(&fc_stats->counters))
+       if (addlist || !list_empty(&fc_stats->counters))
                queue_delayed_work(fc_stats->wq, &fc_stats->work,
                                   fc_stats->sampling_interval);
 
-       llist_for_each_entry(counter, tmplist, addlist)
+       llist_for_each_entry(counter, addlist, addlist)
                mlx5_fc_stats_insert(dev, counter);
 
-       tmplist = llist_del_all(&fc_stats->dellist);
-       llist_for_each_entry_safe(counter, tmp, tmplist, dellist) {
-               list_del(&counter->list);
+       llist_for_each_entry_safe(counter, tmp, dellist, dellist) {
+               mlx5_fc_stats_remove(dev, counter);
 
                mlx5_free_fc(dev, counter);
        }
@@ -258,6 +273,12 @@ err_out:
 }
 EXPORT_SYMBOL(mlx5_fc_create);
 
+u32 mlx5_fc_id(struct mlx5_fc *counter)
+{
+       return counter->id;
+}
+EXPORT_SYMBOL(mlx5_fc_id);
+
 void mlx5_fc_destroy(struct mlx5_core_dev *dev, struct mlx5_fc *counter)
 {
        struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
@@ -266,10 +287,6 @@ void mlx5_fc_destroy(struct mlx5_core_dev *dev, struct mlx5_fc *counter)
                return;
 
        if (counter->aging) {
-               spin_lock(&fc_stats->counters_idr_lock);
-               WARN_ON(!idr_remove(&fc_stats->counters_idr, counter->id));
-               spin_unlock(&fc_stats->counters_idr_lock);
-
                llist_add(&counter->dellist, &fc_stats->dellist);
                mod_delayed_work(fc_stats->wq, &fc_stats->work, 0);
                return;