1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#include <linux/mlx5/driver.h>
34#include <linux/mlx5/fs.h>
35#include <linux/rbtree.h>
36#include <linux/idr_ext.h>
37#include "mlx5_core.h"
38#include "fs_core.h"
39#include "fs_cmd.h"
40
41#define MLX5_FC_STATS_PERIOD msecs_to_jiffies(1000)
42
43#define MLX5_SW_MAX_COUNTERS_BULK BIT(15)
44#define MLX5_FC_POOL_MAX_THRESHOLD BIT(18)
45#define MLX5_FC_POOL_USED_BUFF_RATIO 10
46
47struct mlx5_fc_cache {
48 u64 packets;
49 u64 bytes;
50 u64 lastuse;
51};
52
53struct mlx5_fc {
54 struct list_head list;
55 struct llist_node addlist;
56 struct llist_node dellist;
57
58
59
60
61 u64 lastpackets;
62 u64 lastbytes;
63
64 struct mlx5_fc_bulk *bulk;
65 u32 id;
66 bool aging;
67
68 struct mlx5_fc_cache cache ____cacheline_aligned_in_smp;
69};
70
71static void mlx5_fc_pool_init(struct mlx5_fc_pool *fc_pool, struct mlx5_core_dev *dev);
72static void mlx5_fc_pool_cleanup(struct mlx5_fc_pool *fc_pool);
73static struct mlx5_fc *mlx5_fc_pool_acquire_counter(struct mlx5_fc_pool *fc_pool);
74static void mlx5_fc_pool_release_counter(struct mlx5_fc_pool *fc_pool, struct mlx5_fc *fc);
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108static struct list_head *mlx5_fc_counters_lookup_next(struct mlx5_core_dev *dev,
109 u32 id)
110{
111 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
112 unsigned long next_id = (unsigned long)id + 1;
113 struct mlx5_fc *counter;
114
115 rcu_read_lock();
116
117 while ((counter = idr_get_next_ext(&fc_stats->counters_idr,
118 &next_id)) != NULL &&
119 list_empty(&counter->list))
120 next_id++;
121 rcu_read_unlock();
122
123 return counter ? &counter->list : &fc_stats->counters;
124}
125
126static void mlx5_fc_stats_insert(struct mlx5_core_dev *dev,
127 struct mlx5_fc *counter)
128{
129 struct list_head *next = mlx5_fc_counters_lookup_next(dev, counter->id);
130
131 list_add_tail(&counter->list, next);
132}
133
134static void mlx5_fc_stats_remove(struct mlx5_core_dev *dev,
135 struct mlx5_fc *counter)
136{
137 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
138
139 list_del(&counter->list);
140
141 spin_lock(&fc_stats->counters_idr_lock);
142 idr_remove_ext(&fc_stats->counters_idr, counter->id);
143 spin_unlock(&fc_stats->counters_idr_lock);
144}
145
146static int get_max_bulk_query_len(struct mlx5_core_dev *dev)
147{
148 return min_t(int, MLX5_SW_MAX_COUNTERS_BULK,
149 (1 << MLX5_CAP_GEN(dev, log_max_flow_counter_bulk)));
150}
151
152static void update_counter_cache(int index, u32 *bulk_raw_data,
153 struct mlx5_fc_cache *cache)
154{
155 void *stats = MLX5_ADDR_OF(query_flow_counter_out, bulk_raw_data,
156 flow_statistics[index]);
157 u64 packets = MLX5_GET64(traffic_counter, stats, packets);
158 u64 bytes = MLX5_GET64(traffic_counter, stats, octets);
159
160 if (cache->packets == packets)
161 return;
162
163 cache->packets = packets;
164 cache->bytes = bytes;
165 cache->lastuse = jiffies;
166}
167
168static void mlx5_fc_stats_query_counter_range(struct mlx5_core_dev *dev,
169 struct mlx5_fc *first,
170 u32 last_id)
171{
172 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
173 bool query_more_counters = (first->id <= last_id);
174 int max_bulk_len = get_max_bulk_query_len(dev);
175 u32 *data = fc_stats->bulk_query_out;
176 struct mlx5_fc *counter = first;
177 u32 bulk_base_id;
178 int bulk_len;
179 int err;
180
181 while (query_more_counters) {
182
183 bulk_base_id = counter->id & ~0x3;
184
185
186 bulk_len = min_t(int, max_bulk_len,
187 ALIGN(last_id - bulk_base_id + 1, 4));
188
189 err = mlx5_cmd_fc_bulk_query(dev, bulk_base_id, bulk_len,
190 data);
191 if (err) {
192 mlx5_core_err(dev, "Error doing bulk query: %d\n", err);
193 return;
194 }
195 query_more_counters = false;
196
197 list_for_each_entry_from(counter, &fc_stats->counters, list) {
198 int counter_index = counter->id - bulk_base_id;
199 struct mlx5_fc_cache *cache = &counter->cache;
200
201 if (counter->id >= bulk_base_id + bulk_len) {
202 query_more_counters = true;
203 break;
204 }
205
206 update_counter_cache(counter_index, data, cache);
207 }
208 }
209}
210
211static void mlx5_fc_free(struct mlx5_core_dev *dev, struct mlx5_fc *counter)
212{
213 mlx5_cmd_fc_free(dev, counter->id);
214 kfree(counter);
215}
216
217static void mlx5_fc_release(struct mlx5_core_dev *dev, struct mlx5_fc *counter)
218{
219 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
220
221 if (counter->bulk)
222 mlx5_fc_pool_release_counter(&fc_stats->fc_pool, counter);
223 else
224 mlx5_fc_free(dev, counter);
225}
226
227static void mlx5_fc_stats_work(struct work_struct *work)
228{
229 struct mlx5_core_dev *dev = container_of(work, struct mlx5_core_dev,
230 priv.fc_stats.work.work);
231 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
232
233
234
235 struct llist_node *dellist = llist_del_all(&fc_stats->dellist);
236 struct llist_node *addlist = llist_del_all(&fc_stats->addlist);
237 struct mlx5_fc *counter = NULL, *last = NULL, *tmp;
238 unsigned long now = jiffies;
239
240 if (addlist || !list_empty(&fc_stats->counters))
241 queue_delayed_work(fc_stats->wq, &fc_stats->work,
242 fc_stats->sampling_interval);
243
244 llist_for_each_entry(counter, addlist, addlist)
245 mlx5_fc_stats_insert(dev, counter);
246
247 llist_for_each_entry_safe(counter, tmp, dellist, dellist) {
248 mlx5_fc_stats_remove(dev, counter);
249
250 mlx5_fc_release(dev, counter);
251 }
252
253 if (time_before(now, fc_stats->next_query) ||
254 list_empty(&fc_stats->counters))
255 return;
256 last = list_last_entry(&fc_stats->counters, struct mlx5_fc, list);
257
258 counter = list_first_entry(&fc_stats->counters, struct mlx5_fc,
259 list);
260 if (counter)
261 mlx5_fc_stats_query_counter_range(dev, counter, last->id);
262
263 fc_stats->next_query = now + fc_stats->sampling_interval;
264}
265
266static struct mlx5_fc *mlx5_fc_single_alloc(struct mlx5_core_dev *dev)
267{
268 struct mlx5_fc *counter;
269 int err;
270
271 counter = kzalloc(sizeof(*counter), GFP_KERNEL);
272 if (!counter)
273 return ERR_PTR(-ENOMEM);
274
275 err = mlx5_cmd_fc_alloc(dev, &counter->id);
276 if (err) {
277 kfree(counter);
278 return ERR_PTR(err);
279 }
280
281 return counter;
282}
283
284static struct mlx5_fc *mlx5_fc_acquire(struct mlx5_core_dev *dev, bool aging)
285{
286 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
287 struct mlx5_fc *counter;
288
289 if (aging && MLX5_CAP_GEN(dev, flow_counter_bulk_alloc) != 0) {
290 counter = mlx5_fc_pool_acquire_counter(&fc_stats->fc_pool);
291 if (!IS_ERR(counter))
292 return counter;
293 }
294
295 return mlx5_fc_single_alloc(dev);
296}
297
298struct mlx5_fc *mlx5_fc_create(struct mlx5_core_dev *dev, bool aging)
299{
300 struct mlx5_fc *counter = mlx5_fc_acquire(dev, aging);
301 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
302 int err;
303
304 if (IS_ERR(counter))
305 return counter;
306
307 INIT_LIST_HEAD(&counter->list);
308 counter->aging = aging;
309
310 if (aging) {
311 u32 id = counter->id;
312
313 counter->cache.lastuse = jiffies;
314 counter->lastbytes = counter->cache.bytes;
315 counter->lastpackets = counter->cache.packets;
316
317 idr_preload(GFP_KERNEL);
318 spin_lock(&fc_stats->counters_idr_lock);
319
320 err = idr_alloc_ext(&fc_stats->counters_idr, counter, NULL, id,
321 id + 1, GFP_NOWAIT);
322
323 spin_unlock(&fc_stats->counters_idr_lock);
324 idr_preload_end();
325 if (err)
326 goto err_out_alloc;
327
328 llist_add(&counter->addlist, &fc_stats->addlist);
329
330 mod_delayed_work(fc_stats->wq, &fc_stats->work, 0);
331 }
332
333 return counter;
334
335err_out_alloc:
336 mlx5_fc_release(dev, counter);
337 return ERR_PTR(err);
338}
339EXPORT_SYMBOL(mlx5_fc_create);
340
341u32 mlx5_fc_id(struct mlx5_fc *counter)
342{
343 return counter->id;
344}
345EXPORT_SYMBOL(mlx5_fc_id);
346
347void mlx5_fc_destroy(struct mlx5_core_dev *dev, struct mlx5_fc *counter)
348{
349 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
350
351 if (!counter)
352 return;
353
354 if (counter->aging) {
355 llist_add(&counter->dellist, &fc_stats->dellist);
356 mod_delayed_work(fc_stats->wq, &fc_stats->work, 0);
357 return;
358 }
359
360 mlx5_fc_release(dev, counter);
361}
362EXPORT_SYMBOL(mlx5_fc_destroy);
363
364int mlx5_init_fc_stats(struct mlx5_core_dev *dev)
365{
366 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
367 int max_bulk_len;
368 int max_out_len;
369
370 spin_lock_init(&fc_stats->counters_idr_lock);
371 idr_init_ext(&fc_stats->counters_idr);
372 INIT_LIST_HEAD(&fc_stats->counters);
373 init_llist_head(&fc_stats->addlist);
374 init_llist_head(&fc_stats->dellist);
375
376 max_bulk_len = get_max_bulk_query_len(dev);
377 max_out_len = mlx5_cmd_fc_get_bulk_query_out_len(max_bulk_len);
378 fc_stats->bulk_query_out = kzalloc(max_out_len, GFP_KERNEL);
379 if (!fc_stats->bulk_query_out)
380 return -ENOMEM;
381
382 fc_stats->wq = create_singlethread_workqueue("mlx5_fc");
383 if (!fc_stats->wq)
384 goto err_wq_create;
385
386 fc_stats->sampling_interval = MLX5_FC_STATS_PERIOD;
387 INIT_DELAYED_WORK(&fc_stats->work, mlx5_fc_stats_work);
388
389 mlx5_fc_pool_init(&fc_stats->fc_pool, dev);
390 return 0;
391
392err_wq_create:
393 kfree(fc_stats->bulk_query_out);
394 return -ENOMEM;
395}
396
397void mlx5_cleanup_fc_stats(struct mlx5_core_dev *dev)
398{
399 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
400 struct llist_node *tmplist;
401 struct mlx5_fc *counter;
402 struct mlx5_fc *tmp;
403
404 cancel_delayed_work_sync(&dev->priv.fc_stats.work);
405 destroy_workqueue(dev->priv.fc_stats.wq);
406 dev->priv.fc_stats.wq = NULL;
407
408 tmplist = llist_del_all(&fc_stats->addlist);
409 llist_for_each_entry_safe(counter, tmp, tmplist, addlist)
410 mlx5_fc_release(dev, counter);
411
412 list_for_each_entry_safe(counter, tmp, &fc_stats->counters, list)
413 mlx5_fc_release(dev, counter);
414
415 mlx5_fc_pool_cleanup(&fc_stats->fc_pool);
416 idr_destroy_ext(&fc_stats->counters_idr);
417 kfree(fc_stats->bulk_query_out);
418}
419
420int mlx5_fc_query(struct mlx5_core_dev *dev, struct mlx5_fc *counter,
421 u64 *packets, u64 *bytes)
422{
423 return mlx5_cmd_fc_query(dev, counter->id, packets, bytes);
424}
425EXPORT_SYMBOL(mlx5_fc_query);
426
427u64 mlx5_fc_query_lastuse(struct mlx5_fc *counter)
428{
429 return counter->cache.lastuse;
430}
431
432void mlx5_fc_query_cached(struct mlx5_fc *counter,
433 u64 *bytes, u64 *packets, u64 *lastuse)
434{
435 struct mlx5_fc_cache c;
436
437 c = counter->cache;
438
439 *bytes = c.bytes - counter->lastbytes;
440 *packets = c.packets - counter->lastpackets;
441 *lastuse = c.lastuse;
442
443 counter->lastbytes = c.bytes;
444 counter->lastpackets = c.packets;
445}
446
447void mlx5_fc_queue_stats_work(struct mlx5_core_dev *dev,
448 struct delayed_work *dwork,
449 unsigned long delay)
450{
451 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
452
453 queue_delayed_work(fc_stats->wq, dwork, delay);
454}
455
456void mlx5_fc_update_sampling_interval(struct mlx5_core_dev *dev,
457 unsigned long interval)
458{
459 struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats;
460
461 fc_stats->sampling_interval = min_t(unsigned long, interval,
462 fc_stats->sampling_interval);
463}
464
465
466
467struct mlx5_fc_bulk {
468 struct list_head pool_list;
469 u32 base_id;
470 int bulk_len;
471 unsigned long *bitmask;
472 struct mlx5_fc fcs[0];
473};
474
475static void mlx5_fc_init(struct mlx5_fc *counter, struct mlx5_fc_bulk *bulk,
476 u32 id)
477{
478 counter->bulk = bulk;
479 counter->id = id;
480}
481
482static int mlx5_fc_bulk_get_free_fcs_amount(struct mlx5_fc_bulk *bulk)
483{
484 return bitmap_weight(bulk->bitmask, bulk->bulk_len);
485}
486
487static struct mlx5_fc_bulk *mlx5_fc_bulk_create(struct mlx5_core_dev *dev)
488{
489 enum mlx5_fc_bulk_alloc_bitmask alloc_bitmask;
490 struct mlx5_fc_bulk *bulk;
491 int err = -ENOMEM;
492 int bulk_len;
493 u32 base_id;
494 int i;
495
496 alloc_bitmask = MLX5_CAP_GEN(dev, flow_counter_bulk_alloc);
497 bulk_len = alloc_bitmask > 0 ? MLX5_FC_BULK_NUM_FCS(alloc_bitmask) : 1;
498
499 bulk = kzalloc(sizeof(*bulk) + bulk_len * sizeof(struct mlx5_fc),
500 GFP_KERNEL);
501 if (!bulk)
502 goto err_alloc_bulk;
503
504 bulk->bitmask = kcalloc(BITS_TO_LONGS(bulk_len), sizeof(unsigned long),
505 GFP_KERNEL);
506 if (!bulk->bitmask)
507 goto err_alloc_bitmask;
508
509 err = mlx5_cmd_fc_bulk_alloc(dev, alloc_bitmask, &base_id);
510 if (err)
511 goto err_mlx5_cmd_bulk_alloc;
512
513 bulk->base_id = base_id;
514 bulk->bulk_len = bulk_len;
515 for (i = 0; i < bulk_len; i++) {
516 mlx5_fc_init(&bulk->fcs[i], bulk, base_id + i);
517 set_bit(i, bulk->bitmask);
518 }
519
520 return bulk;
521
522err_mlx5_cmd_bulk_alloc:
523 kfree(bulk->bitmask);
524err_alloc_bitmask:
525 kfree(bulk);
526err_alloc_bulk:
527 return ERR_PTR(err);
528}
529
530static int
531mlx5_fc_bulk_destroy(struct mlx5_core_dev *dev, struct mlx5_fc_bulk *bulk)
532{
533 if (mlx5_fc_bulk_get_free_fcs_amount(bulk) < bulk->bulk_len) {
534 mlx5_core_err(dev, "Freeing bulk before all counters were released\n");
535 return -EBUSY;
536 }
537
538 mlx5_cmd_fc_free(dev, bulk->base_id);
539 kfree(bulk->bitmask);
540 kfree(bulk);
541
542 return 0;
543}
544
545static struct mlx5_fc *mlx5_fc_bulk_acquire_fc(struct mlx5_fc_bulk *bulk)
546{
547 int free_fc_index = find_first_bit(bulk->bitmask, bulk->bulk_len);
548
549 if (free_fc_index >= bulk->bulk_len)
550 return ERR_PTR(-ENOSPC);
551
552 clear_bit(free_fc_index, bulk->bitmask);
553 return &bulk->fcs[free_fc_index];
554}
555
556static int mlx5_fc_bulk_release_fc(struct mlx5_fc_bulk *bulk, struct mlx5_fc *fc)
557{
558 int fc_index = fc->id - bulk->base_id;
559
560 if (test_bit(fc_index, bulk->bitmask))
561 return -EINVAL;
562
563 set_bit(fc_index, bulk->bitmask);
564 return 0;
565}
566
567
568
569static void mlx5_fc_pool_init(struct mlx5_fc_pool *fc_pool, struct mlx5_core_dev *dev)
570{
571 fc_pool->dev = dev;
572 mutex_init(&fc_pool->pool_lock);
573 INIT_LIST_HEAD(&fc_pool->fully_used);
574 INIT_LIST_HEAD(&fc_pool->partially_used);
575 INIT_LIST_HEAD(&fc_pool->unused);
576 fc_pool->available_fcs = 0;
577 fc_pool->used_fcs = 0;
578 fc_pool->threshold = 0;
579}
580
581static void mlx5_fc_pool_cleanup(struct mlx5_fc_pool *fc_pool)
582{
583 struct mlx5_core_dev *dev = fc_pool->dev;
584 struct mlx5_fc_bulk *bulk;
585 struct mlx5_fc_bulk *tmp;
586
587 list_for_each_entry_safe(bulk, tmp, &fc_pool->fully_used, pool_list)
588 mlx5_fc_bulk_destroy(dev, bulk);
589 list_for_each_entry_safe(bulk, tmp, &fc_pool->partially_used, pool_list)
590 mlx5_fc_bulk_destroy(dev, bulk);
591 list_for_each_entry_safe(bulk, tmp, &fc_pool->unused, pool_list)
592 mlx5_fc_bulk_destroy(dev, bulk);
593}
594
595static void mlx5_fc_pool_update_threshold(struct mlx5_fc_pool *fc_pool)
596{
597 fc_pool->threshold = min_t(int, MLX5_FC_POOL_MAX_THRESHOLD,
598 fc_pool->used_fcs / MLX5_FC_POOL_USED_BUFF_RATIO);
599}
600
601static struct mlx5_fc_bulk *
602mlx5_fc_pool_alloc_new_bulk(struct mlx5_fc_pool *fc_pool)
603{
604 struct mlx5_core_dev *dev = fc_pool->dev;
605 struct mlx5_fc_bulk *new_bulk;
606
607 new_bulk = mlx5_fc_bulk_create(dev);
608 if (!IS_ERR(new_bulk))
609 fc_pool->available_fcs += new_bulk->bulk_len;
610 mlx5_fc_pool_update_threshold(fc_pool);
611 return new_bulk;
612}
613
614static void
615mlx5_fc_pool_free_bulk(struct mlx5_fc_pool *fc_pool, struct mlx5_fc_bulk *bulk)
616{
617 struct mlx5_core_dev *dev = fc_pool->dev;
618
619 fc_pool->available_fcs -= bulk->bulk_len;
620 mlx5_fc_bulk_destroy(dev, bulk);
621 mlx5_fc_pool_update_threshold(fc_pool);
622}
623
624static struct mlx5_fc *
625mlx5_fc_pool_acquire_from_list(struct list_head *src_list,
626 struct list_head *next_list,
627 bool move_non_full_bulk)
628{
629 struct mlx5_fc_bulk *bulk;
630 struct mlx5_fc *fc;
631
632 if (list_empty(src_list))
633 return ERR_PTR(-ENODATA);
634
635 bulk = list_first_entry(src_list, struct mlx5_fc_bulk, pool_list);
636 fc = mlx5_fc_bulk_acquire_fc(bulk);
637 if (move_non_full_bulk || mlx5_fc_bulk_get_free_fcs_amount(bulk) == 0)
638 list_move(&bulk->pool_list, next_list);
639 return fc;
640}
641
642static struct mlx5_fc *
643mlx5_fc_pool_acquire_counter(struct mlx5_fc_pool *fc_pool)
644{
645 struct mlx5_fc_bulk *new_bulk;
646 struct mlx5_fc *fc;
647
648 mutex_lock(&fc_pool->pool_lock);
649
650 fc = mlx5_fc_pool_acquire_from_list(&fc_pool->partially_used,
651 &fc_pool->fully_used, false);
652 if (IS_ERR(fc))
653 fc = mlx5_fc_pool_acquire_from_list(&fc_pool->unused,
654 &fc_pool->partially_used,
655 true);
656 if (IS_ERR(fc)) {
657 new_bulk = mlx5_fc_pool_alloc_new_bulk(fc_pool);
658 if (IS_ERR(new_bulk)) {
659 fc = ERR_CAST(new_bulk);
660 goto out;
661 }
662 fc = mlx5_fc_bulk_acquire_fc(new_bulk);
663 list_add(&new_bulk->pool_list, &fc_pool->partially_used);
664 }
665 fc_pool->available_fcs--;
666 fc_pool->used_fcs++;
667
668out:
669 mutex_unlock(&fc_pool->pool_lock);
670 return fc;
671}
672
673static void
674mlx5_fc_pool_release_counter(struct mlx5_fc_pool *fc_pool, struct mlx5_fc *fc)
675{
676 struct mlx5_core_dev *dev = fc_pool->dev;
677 struct mlx5_fc_bulk *bulk = fc->bulk;
678 int bulk_free_fcs_amount;
679
680 mutex_lock(&fc_pool->pool_lock);
681
682 if (mlx5_fc_bulk_release_fc(bulk, fc)) {
683 mlx5_core_warn(dev, "Attempted to release a counter which is not acquired\n");
684 goto unlock;
685 }
686
687 fc_pool->available_fcs++;
688 fc_pool->used_fcs--;
689
690 bulk_free_fcs_amount = mlx5_fc_bulk_get_free_fcs_amount(bulk);
691 if (bulk_free_fcs_amount == 1)
692 list_move_tail(&bulk->pool_list, &fc_pool->partially_used);
693 if (bulk_free_fcs_amount == bulk->bulk_len) {
694 list_del(&bulk->pool_list);
695 if (fc_pool->available_fcs > fc_pool->threshold)
696 mlx5_fc_pool_free_bulk(fc_pool, bulk);
697 else
698 list_add(&bulk->pool_list, &fc_pool->unused);
699 }
700
701unlock:
702 mutex_unlock(&fc_pool->pool_lock);
703}
704