net/mlx5: Add flow counters idr
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / fs_counters.c
index 90ebfee37508ca2073ed0372ee8632911d28b068..09206c4acd9a890d384b156d74f729910e4a43aa 100644 (file)
@@ -77,13 +77,18 @@ static struct list_head *mlx5_fc_counters_lookup_next(struct mlx5_core_dev *dev,
                                                      u32 id)
 {
        struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
+       unsigned long next_id = (unsigned long)id + 1;
        struct mlx5_fc *counter;
 
-       list_for_each_entry(counter, &fc_stats->counters, list)
-               if (counter->id > id)
-                       return &counter->list;
+       rcu_read_lock();
+       /* skip counters that are in idr, but not yet in counters list */
+       while ((counter = idr_get_next_ul(&fc_stats->counters_idr,
+                                         &next_id)) != NULL &&
+              list_empty(&counter->list))
+               next_id++;
+       rcu_read_unlock();
 
-       return &fc_stats->counters;
+       return counter ? &counter->list : &fc_stats->counters;
 }
 
 static void mlx5_fc_stats_insert(struct mlx5_core_dev *dev,
@@ -214,15 +219,29 @@ struct mlx5_fc *mlx5_fc_create(struct mlx5_core_dev *dev, bool aging)
        counter = kzalloc(sizeof(*counter), GFP_KERNEL);
        if (!counter)
                return ERR_PTR(-ENOMEM);
+       INIT_LIST_HEAD(&counter->list);
 
        err = mlx5_cmd_fc_alloc(dev, &counter->id);
        if (err)
                goto err_out;
 
        if (aging) {
+               u32 id = counter->id;
+
                counter->cache.lastuse = jiffies;
                counter->aging = true;
 
+               idr_preload(GFP_KERNEL);
+               spin_lock(&fc_stats->counters_idr_lock);
+
+               err = idr_alloc_u32(&fc_stats->counters_idr, counter, &id, id,
+                                   GFP_NOWAIT);
+
+               spin_unlock(&fc_stats->counters_idr_lock);
+               idr_preload_end();
+               if (err)
+                       goto err_out_alloc;
+
                llist_add(&counter->addlist, &fc_stats->addlist);
 
                mod_delayed_work(fc_stats->wq, &fc_stats->work, 0);
@@ -230,6 +249,8 @@ struct mlx5_fc *mlx5_fc_create(struct mlx5_core_dev *dev, bool aging)
 
        return counter;
 
+err_out_alloc:
+       mlx5_cmd_fc_free(dev, counter->id);
 err_out:
        kfree(counter);
 
@@ -245,6 +266,10 @@ 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;
@@ -258,6 +283,8 @@ int mlx5_init_fc_stats(struct mlx5_core_dev *dev)
 {
        struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
 
+       spin_lock_init(&fc_stats->counters_idr_lock);
+       idr_init(&fc_stats->counters_idr);
        INIT_LIST_HEAD(&fc_stats->counters);
        init_llist_head(&fc_stats->addlist);
        init_llist_head(&fc_stats->dellist);
@@ -283,6 +310,8 @@ void mlx5_cleanup_fc_stats(struct mlx5_core_dev *dev)
        destroy_workqueue(dev->priv.fc_stats.wq);
        dev->priv.fc_stats.wq = NULL;
 
+       idr_destroy(&fc_stats->counters_idr);
+
        tmplist = llist_del_all(&fc_stats->addlist);
        llist_for_each_entry_safe(counter, tmp, tmplist, addlist)
                mlx5_free_fc(dev, counter);