1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2021 Marvell International Ltd. All rights reserved */
5 #include "prestera_hw.h"
6 #include "prestera_acl.h"
7 #include "prestera_counter.h"
9 #define COUNTER_POLL_TIME (msecs_to_jiffies(1000))
10 #define COUNTER_RESCHED_TIME (msecs_to_jiffies(50))
11 #define COUNTER_BULK_SIZE (256)
13 struct prestera_counter {
14 struct prestera_switch *sw;
15 struct delayed_work stats_dw;
16 struct mutex mtx; /* protect block_list */
17 struct prestera_counter_block **block_list;
24 struct prestera_counter_block {
25 struct list_head list;
30 struct idr counter_idr;
32 struct mutex mtx; /* protect stats and counter_idr */
33 struct prestera_counter_stats *stats;
40 COUNTER_FLAG_READY = 0,
41 COUNTER_FLAG_INVALID = 1
45 prestera_counter_is_ready(struct prestera_counter_block *block, u32 id)
47 return block->counter_flag[id - block->offset] == COUNTER_FLAG_READY;
50 static void prestera_counter_lock(struct prestera_counter *counter)
52 mutex_lock(&counter->mtx);
55 static void prestera_counter_unlock(struct prestera_counter *counter)
57 mutex_unlock(&counter->mtx);
60 static void prestera_counter_block_lock(struct prestera_counter_block *block)
62 mutex_lock(&block->mtx);
65 static void prestera_counter_block_unlock(struct prestera_counter_block *block)
67 mutex_unlock(&block->mtx);
70 static bool prestera_counter_block_incref(struct prestera_counter_block *block)
72 return refcount_inc_not_zero(&block->refcnt);
75 static bool prestera_counter_block_decref(struct prestera_counter_block *block)
77 return refcount_dec_and_test(&block->refcnt);
80 /* must be called with prestera_counter_block_lock() */
81 static void prestera_counter_stats_clear(struct prestera_counter_block *block,
84 memset(&block->stats[counter_id - block->offset], 0,
85 sizeof(*block->stats));
88 static struct prestera_counter_block *
89 prestera_counter_block_lookup_not_full(struct prestera_counter *counter,
94 prestera_counter_lock(counter);
95 for (i = 0; i < counter->block_list_len; i++) {
96 if (counter->block_list[i] &&
97 counter->block_list[i]->client == client &&
98 !counter->block_list[i]->full &&
99 prestera_counter_block_incref(counter->block_list[i])) {
100 prestera_counter_unlock(counter);
101 return counter->block_list[i];
104 prestera_counter_unlock(counter);
109 static int prestera_counter_block_list_add(struct prestera_counter *counter,
110 struct prestera_counter_block *block)
112 struct prestera_counter_block **arr;
115 prestera_counter_lock(counter);
117 for (i = 0; i < counter->block_list_len; i++) {
118 if (counter->block_list[i])
121 counter->block_list[i] = block;
122 prestera_counter_unlock(counter);
126 arr = krealloc(counter->block_list, (counter->block_list_len + 1) *
127 sizeof(*counter->block_list), GFP_KERNEL);
129 prestera_counter_unlock(counter);
133 counter->block_list = arr;
134 counter->block_list[counter->block_list_len] = block;
135 counter->block_list_len++;
136 prestera_counter_unlock(counter);
140 static struct prestera_counter_block *
141 prestera_counter_block_get(struct prestera_counter *counter, u32 client)
143 struct prestera_counter_block *block;
146 block = prestera_counter_block_lookup_not_full(counter, client);
150 block = kzalloc(sizeof(*block), GFP_KERNEL);
152 return ERR_PTR(-ENOMEM);
154 err = prestera_hw_counter_block_get(counter->sw, client,
155 &block->id, &block->offset,
156 &block->num_counters);
160 block->stats = kcalloc(block->num_counters,
161 sizeof(*block->stats), GFP_KERNEL);
167 block->counter_flag = kcalloc(block->num_counters,
168 sizeof(*block->counter_flag),
170 if (!block->counter_flag) {
175 block->client = client;
176 mutex_init(&block->mtx);
177 refcount_set(&block->refcnt, 1);
178 idr_init_base(&block->counter_idr, block->offset);
180 err = prestera_counter_block_list_add(counter, block);
187 idr_destroy(&block->counter_idr);
188 mutex_destroy(&block->mtx);
189 kfree(block->counter_flag);
193 prestera_hw_counter_block_release(counter->sw, block->id);
199 static void prestera_counter_block_put(struct prestera_counter *counter,
200 struct prestera_counter_block *block)
204 if (!prestera_counter_block_decref(block))
207 prestera_counter_lock(counter);
208 for (i = 0; i < counter->block_list_len; i++) {
209 if (counter->block_list[i] &&
210 counter->block_list[i]->id == block->id) {
211 counter->block_list[i] = NULL;
215 prestera_counter_unlock(counter);
217 WARN_ON(!idr_is_empty(&block->counter_idr));
219 prestera_hw_counter_block_release(counter->sw, block->id);
220 idr_destroy(&block->counter_idr);
221 mutex_destroy(&block->mtx);
226 static int prestera_counter_get_vacant(struct prestera_counter_block *block,
234 prestera_counter_block_lock(block);
235 free_id = idr_alloc_cyclic(&block->counter_idr, NULL, block->offset,
236 block->offset + block->num_counters,
239 if (free_id == -ENOSPC)
242 prestera_counter_block_unlock(block);
246 prestera_counter_block_unlock(block);
251 int prestera_counter_get(struct prestera_counter *counter, u32 client,
252 struct prestera_counter_block **bl, u32 *counter_id)
254 struct prestera_counter_block *block;
259 block = prestera_counter_block_get(counter, client);
261 return PTR_ERR(block);
263 err = prestera_counter_get_vacant(block, &id);
265 prestera_counter_block_put(counter, block);
273 prestera_counter_block_lock(block);
274 if (block->is_updating)
275 block->counter_flag[id - block->offset] = COUNTER_FLAG_INVALID;
276 prestera_counter_block_unlock(block);
284 void prestera_counter_put(struct prestera_counter *counter,
285 struct prestera_counter_block *block, u32 counter_id)
290 prestera_counter_block_lock(block);
291 idr_remove(&block->counter_idr, counter_id);
293 prestera_counter_stats_clear(block, counter_id);
294 prestera_counter_block_unlock(block);
296 prestera_hw_counter_clear(counter->sw, block->id, counter_id);
297 prestera_counter_block_put(counter, block);
300 static u32 prestera_counter_block_idx_next(struct prestera_counter *counter,
303 u32 idx, i, start = curr_idx + 1;
305 prestera_counter_lock(counter);
306 for (i = 0; i < counter->block_list_len; i++) {
307 idx = (start + i) % counter->block_list_len;
308 if (!counter->block_list[idx])
311 prestera_counter_unlock(counter);
314 prestera_counter_unlock(counter);
319 static struct prestera_counter_block *
320 prestera_counter_block_get_by_idx(struct prestera_counter *counter, u32 idx)
322 if (idx >= counter->block_list_len)
325 prestera_counter_lock(counter);
327 if (!counter->block_list[idx] ||
328 !prestera_counter_block_incref(counter->block_list[idx])) {
329 prestera_counter_unlock(counter);
333 prestera_counter_unlock(counter);
334 return counter->block_list[idx];
337 static void prestera_counter_stats_work(struct work_struct *work)
339 struct delayed_work *dl_work =
340 container_of(work, struct delayed_work, work);
341 struct prestera_counter *counter =
342 container_of(dl_work, struct prestera_counter, stats_dw);
343 struct prestera_counter_block *block;
344 u32 resched_time = COUNTER_POLL_TIME;
345 u32 count = COUNTER_BULK_SIZE;
350 block = prestera_counter_block_get_by_idx(counter, counter->curr_idx);
352 if (counter->is_fetching)
358 if (!counter->is_fetching) {
359 err = prestera_hw_counter_trigger(counter->sw, block->id);
363 prestera_counter_block_lock(block);
364 block->is_updating = true;
365 prestera_counter_block_unlock(block);
367 counter->is_fetching = true;
368 counter->total_read = 0;
369 resched_time = COUNTER_RESCHED_TIME;
373 prestera_counter_block_lock(block);
374 err = prestera_hw_counters_get(counter->sw, counter->total_read,
376 &block->stats[counter->total_read]);
377 prestera_counter_block_unlock(block);
381 counter->total_read += count;
382 if (!done || counter->total_read < block->num_counters) {
383 resched_time = COUNTER_RESCHED_TIME;
387 for (i = 0; i < block->num_counters; i++) {
388 if (block->counter_flag[i] == COUNTER_FLAG_INVALID) {
389 prestera_counter_block_lock(block);
390 block->counter_flag[i] = COUNTER_FLAG_READY;
391 memset(&block->stats[i], 0, sizeof(*block->stats));
392 prestera_counter_block_unlock(block);
396 prestera_counter_block_lock(block);
397 block->is_updating = false;
398 prestera_counter_block_unlock(block);
402 prestera_hw_counter_abort(counter->sw);
404 counter->is_fetching = false;
406 prestera_counter_block_idx_next(counter, counter->curr_idx);
409 prestera_counter_block_put(counter, block);
411 schedule_delayed_work(&counter->stats_dw, resched_time);
414 /* Can be executed without rtnl_lock().
415 * So pay attention when something changing.
417 int prestera_counter_stats_get(struct prestera_counter *counter,
418 struct prestera_counter_block *block,
419 u32 counter_id, u64 *packets, u64 *bytes)
421 if (!block || !prestera_counter_is_ready(block, counter_id)) {
427 prestera_counter_block_lock(block);
428 *packets = block->stats[counter_id - block->offset].packets;
429 *bytes = block->stats[counter_id - block->offset].bytes;
431 prestera_counter_stats_clear(block, counter_id);
432 prestera_counter_block_unlock(block);
437 int prestera_counter_init(struct prestera_switch *sw)
439 struct prestera_counter *counter;
441 counter = kzalloc(sizeof(*counter), GFP_KERNEL);
445 counter->block_list = kzalloc(sizeof(*counter->block_list), GFP_KERNEL);
446 if (!counter->block_list) {
451 mutex_init(&counter->mtx);
452 counter->block_list_len = 1;
454 sw->counter = counter;
456 INIT_DELAYED_WORK(&counter->stats_dw, prestera_counter_stats_work);
457 schedule_delayed_work(&counter->stats_dw, COUNTER_POLL_TIME);
462 void prestera_counter_fini(struct prestera_switch *sw)
464 struct prestera_counter *counter = sw->counter;
467 cancel_delayed_work_sync(&counter->stats_dw);
469 for (i = 0; i < counter->block_list_len; i++)
470 WARN_ON(counter->block_list[i]);
472 mutex_destroy(&counter->mtx);
473 kfree(counter->block_list);