162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2021 Marvell International Ltd. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "prestera.h" 562306a36Sopenharmony_ci#include "prestera_hw.h" 662306a36Sopenharmony_ci#include "prestera_acl.h" 762306a36Sopenharmony_ci#include "prestera_counter.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define COUNTER_POLL_TIME (msecs_to_jiffies(1000)) 1062306a36Sopenharmony_ci#define COUNTER_RESCHED_TIME (msecs_to_jiffies(50)) 1162306a36Sopenharmony_ci#define COUNTER_BULK_SIZE (256) 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistruct prestera_counter { 1462306a36Sopenharmony_ci struct prestera_switch *sw; 1562306a36Sopenharmony_ci struct delayed_work stats_dw; 1662306a36Sopenharmony_ci struct mutex mtx; /* protect block_list */ 1762306a36Sopenharmony_ci struct prestera_counter_block **block_list; 1862306a36Sopenharmony_ci u32 total_read; 1962306a36Sopenharmony_ci u32 block_list_len; 2062306a36Sopenharmony_ci u32 curr_idx; 2162306a36Sopenharmony_ci bool is_fetching; 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct prestera_counter_block { 2562306a36Sopenharmony_ci struct list_head list; 2662306a36Sopenharmony_ci u32 id; 2762306a36Sopenharmony_ci u32 offset; 2862306a36Sopenharmony_ci u32 num_counters; 2962306a36Sopenharmony_ci u32 client; 3062306a36Sopenharmony_ci struct idr counter_idr; 3162306a36Sopenharmony_ci refcount_t refcnt; 3262306a36Sopenharmony_ci struct mutex mtx; /* protect stats and counter_idr */ 3362306a36Sopenharmony_ci struct prestera_counter_stats *stats; 3462306a36Sopenharmony_ci u8 *counter_flag; 3562306a36Sopenharmony_ci bool is_updating; 3662306a36Sopenharmony_ci bool full; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cienum { 4062306a36Sopenharmony_ci COUNTER_FLAG_READY = 0, 4162306a36Sopenharmony_ci COUNTER_FLAG_INVALID = 1 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic bool 4562306a36Sopenharmony_ciprestera_counter_is_ready(struct prestera_counter_block *block, u32 id) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci return block->counter_flag[id - block->offset] == COUNTER_FLAG_READY; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void prestera_counter_lock(struct prestera_counter *counter) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci mutex_lock(&counter->mtx); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void prestera_counter_unlock(struct prestera_counter *counter) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci mutex_unlock(&counter->mtx); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void prestera_counter_block_lock(struct prestera_counter_block *block) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci mutex_lock(&block->mtx); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void prestera_counter_block_unlock(struct prestera_counter_block *block) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci mutex_unlock(&block->mtx); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic bool prestera_counter_block_incref(struct prestera_counter_block *block) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return refcount_inc_not_zero(&block->refcnt); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic bool prestera_counter_block_decref(struct prestera_counter_block *block) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci return refcount_dec_and_test(&block->refcnt); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* must be called with prestera_counter_block_lock() */ 8162306a36Sopenharmony_cistatic void prestera_counter_stats_clear(struct prestera_counter_block *block, 8262306a36Sopenharmony_ci u32 counter_id) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci memset(&block->stats[counter_id - block->offset], 0, 8562306a36Sopenharmony_ci sizeof(*block->stats)); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic struct prestera_counter_block * 8962306a36Sopenharmony_ciprestera_counter_block_lookup_not_full(struct prestera_counter *counter, 9062306a36Sopenharmony_ci u32 client) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci u32 i; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci prestera_counter_lock(counter); 9562306a36Sopenharmony_ci for (i = 0; i < counter->block_list_len; i++) { 9662306a36Sopenharmony_ci if (counter->block_list[i] && 9762306a36Sopenharmony_ci counter->block_list[i]->client == client && 9862306a36Sopenharmony_ci !counter->block_list[i]->full && 9962306a36Sopenharmony_ci prestera_counter_block_incref(counter->block_list[i])) { 10062306a36Sopenharmony_ci prestera_counter_unlock(counter); 10162306a36Sopenharmony_ci return counter->block_list[i]; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci prestera_counter_unlock(counter); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return NULL; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int prestera_counter_block_list_add(struct prestera_counter *counter, 11062306a36Sopenharmony_ci struct prestera_counter_block *block) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct prestera_counter_block **arr; 11362306a36Sopenharmony_ci u32 i; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci prestera_counter_lock(counter); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci for (i = 0; i < counter->block_list_len; i++) { 11862306a36Sopenharmony_ci if (counter->block_list[i]) 11962306a36Sopenharmony_ci continue; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci counter->block_list[i] = block; 12262306a36Sopenharmony_ci prestera_counter_unlock(counter); 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci arr = krealloc(counter->block_list, (counter->block_list_len + 1) * 12762306a36Sopenharmony_ci sizeof(*counter->block_list), GFP_KERNEL); 12862306a36Sopenharmony_ci if (!arr) { 12962306a36Sopenharmony_ci prestera_counter_unlock(counter); 13062306a36Sopenharmony_ci return -ENOMEM; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci counter->block_list = arr; 13462306a36Sopenharmony_ci counter->block_list[counter->block_list_len] = block; 13562306a36Sopenharmony_ci counter->block_list_len++; 13662306a36Sopenharmony_ci prestera_counter_unlock(counter); 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic struct prestera_counter_block * 14162306a36Sopenharmony_ciprestera_counter_block_get(struct prestera_counter *counter, u32 client) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct prestera_counter_block *block; 14462306a36Sopenharmony_ci int err; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci block = prestera_counter_block_lookup_not_full(counter, client); 14762306a36Sopenharmony_ci if (block) 14862306a36Sopenharmony_ci return block; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci block = kzalloc(sizeof(*block), GFP_KERNEL); 15162306a36Sopenharmony_ci if (!block) 15262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci err = prestera_hw_counter_block_get(counter->sw, client, 15562306a36Sopenharmony_ci &block->id, &block->offset, 15662306a36Sopenharmony_ci &block->num_counters); 15762306a36Sopenharmony_ci if (err) 15862306a36Sopenharmony_ci goto err_block; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci block->stats = kcalloc(block->num_counters, 16162306a36Sopenharmony_ci sizeof(*block->stats), GFP_KERNEL); 16262306a36Sopenharmony_ci if (!block->stats) { 16362306a36Sopenharmony_ci err = -ENOMEM; 16462306a36Sopenharmony_ci goto err_stats; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci block->counter_flag = kcalloc(block->num_counters, 16862306a36Sopenharmony_ci sizeof(*block->counter_flag), 16962306a36Sopenharmony_ci GFP_KERNEL); 17062306a36Sopenharmony_ci if (!block->counter_flag) { 17162306a36Sopenharmony_ci err = -ENOMEM; 17262306a36Sopenharmony_ci goto err_flag; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci block->client = client; 17662306a36Sopenharmony_ci mutex_init(&block->mtx); 17762306a36Sopenharmony_ci refcount_set(&block->refcnt, 1); 17862306a36Sopenharmony_ci idr_init_base(&block->counter_idr, block->offset); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci err = prestera_counter_block_list_add(counter, block); 18162306a36Sopenharmony_ci if (err) 18262306a36Sopenharmony_ci goto err_list_add; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return block; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cierr_list_add: 18762306a36Sopenharmony_ci idr_destroy(&block->counter_idr); 18862306a36Sopenharmony_ci mutex_destroy(&block->mtx); 18962306a36Sopenharmony_ci kfree(block->counter_flag); 19062306a36Sopenharmony_cierr_flag: 19162306a36Sopenharmony_ci kfree(block->stats); 19262306a36Sopenharmony_cierr_stats: 19362306a36Sopenharmony_ci prestera_hw_counter_block_release(counter->sw, block->id); 19462306a36Sopenharmony_cierr_block: 19562306a36Sopenharmony_ci kfree(block); 19662306a36Sopenharmony_ci return ERR_PTR(err); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void prestera_counter_block_put(struct prestera_counter *counter, 20062306a36Sopenharmony_ci struct prestera_counter_block *block) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci u32 i; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!prestera_counter_block_decref(block)) 20562306a36Sopenharmony_ci return; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci prestera_counter_lock(counter); 20862306a36Sopenharmony_ci for (i = 0; i < counter->block_list_len; i++) { 20962306a36Sopenharmony_ci if (counter->block_list[i] && 21062306a36Sopenharmony_ci counter->block_list[i]->id == block->id) { 21162306a36Sopenharmony_ci counter->block_list[i] = NULL; 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci prestera_counter_unlock(counter); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci WARN_ON(!idr_is_empty(&block->counter_idr)); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci prestera_hw_counter_block_release(counter->sw, block->id); 22062306a36Sopenharmony_ci idr_destroy(&block->counter_idr); 22162306a36Sopenharmony_ci mutex_destroy(&block->mtx); 22262306a36Sopenharmony_ci kfree(block->stats); 22362306a36Sopenharmony_ci kfree(block); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int prestera_counter_get_vacant(struct prestera_counter_block *block, 22762306a36Sopenharmony_ci u32 *id) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci int free_id; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (block->full) 23262306a36Sopenharmony_ci return -ENOSPC; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci prestera_counter_block_lock(block); 23562306a36Sopenharmony_ci free_id = idr_alloc_cyclic(&block->counter_idr, NULL, block->offset, 23662306a36Sopenharmony_ci block->offset + block->num_counters, 23762306a36Sopenharmony_ci GFP_KERNEL); 23862306a36Sopenharmony_ci if (free_id < 0) { 23962306a36Sopenharmony_ci if (free_id == -ENOSPC) 24062306a36Sopenharmony_ci block->full = true; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci prestera_counter_block_unlock(block); 24362306a36Sopenharmony_ci return free_id; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci *id = free_id; 24662306a36Sopenharmony_ci prestera_counter_block_unlock(block); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ciint prestera_counter_get(struct prestera_counter *counter, u32 client, 25262306a36Sopenharmony_ci struct prestera_counter_block **bl, u32 *counter_id) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct prestera_counter_block *block; 25562306a36Sopenharmony_ci int err; 25662306a36Sopenharmony_ci u32 id; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciget_next_block: 25962306a36Sopenharmony_ci block = prestera_counter_block_get(counter, client); 26062306a36Sopenharmony_ci if (IS_ERR(block)) 26162306a36Sopenharmony_ci return PTR_ERR(block); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci err = prestera_counter_get_vacant(block, &id); 26462306a36Sopenharmony_ci if (err) { 26562306a36Sopenharmony_ci prestera_counter_block_put(counter, block); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (err == -ENOSPC) 26862306a36Sopenharmony_ci goto get_next_block; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return err; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci prestera_counter_block_lock(block); 27462306a36Sopenharmony_ci if (block->is_updating) 27562306a36Sopenharmony_ci block->counter_flag[id - block->offset] = COUNTER_FLAG_INVALID; 27662306a36Sopenharmony_ci prestera_counter_block_unlock(block); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci *counter_id = id; 27962306a36Sopenharmony_ci *bl = block; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_civoid prestera_counter_put(struct prestera_counter *counter, 28562306a36Sopenharmony_ci struct prestera_counter_block *block, u32 counter_id) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci if (!block) 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci prestera_counter_block_lock(block); 29162306a36Sopenharmony_ci idr_remove(&block->counter_idr, counter_id); 29262306a36Sopenharmony_ci block->full = false; 29362306a36Sopenharmony_ci prestera_counter_stats_clear(block, counter_id); 29462306a36Sopenharmony_ci prestera_counter_block_unlock(block); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci prestera_hw_counter_clear(counter->sw, block->id, counter_id); 29762306a36Sopenharmony_ci prestera_counter_block_put(counter, block); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic u32 prestera_counter_block_idx_next(struct prestera_counter *counter, 30162306a36Sopenharmony_ci u32 curr_idx) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci u32 idx, i, start = curr_idx + 1; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci prestera_counter_lock(counter); 30662306a36Sopenharmony_ci for (i = 0; i < counter->block_list_len; i++) { 30762306a36Sopenharmony_ci idx = (start + i) % counter->block_list_len; 30862306a36Sopenharmony_ci if (!counter->block_list[idx]) 30962306a36Sopenharmony_ci continue; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci prestera_counter_unlock(counter); 31262306a36Sopenharmony_ci return idx; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci prestera_counter_unlock(counter); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic struct prestera_counter_block * 32062306a36Sopenharmony_ciprestera_counter_block_get_by_idx(struct prestera_counter *counter, u32 idx) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci if (idx >= counter->block_list_len) 32362306a36Sopenharmony_ci return NULL; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci prestera_counter_lock(counter); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (!counter->block_list[idx] || 32862306a36Sopenharmony_ci !prestera_counter_block_incref(counter->block_list[idx])) { 32962306a36Sopenharmony_ci prestera_counter_unlock(counter); 33062306a36Sopenharmony_ci return NULL; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci prestera_counter_unlock(counter); 33462306a36Sopenharmony_ci return counter->block_list[idx]; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void prestera_counter_stats_work(struct work_struct *work) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct delayed_work *dl_work = 34062306a36Sopenharmony_ci container_of(work, struct delayed_work, work); 34162306a36Sopenharmony_ci struct prestera_counter *counter = 34262306a36Sopenharmony_ci container_of(dl_work, struct prestera_counter, stats_dw); 34362306a36Sopenharmony_ci struct prestera_counter_block *block; 34462306a36Sopenharmony_ci u32 resched_time = COUNTER_POLL_TIME; 34562306a36Sopenharmony_ci u32 count = COUNTER_BULK_SIZE; 34662306a36Sopenharmony_ci bool done = false; 34762306a36Sopenharmony_ci int err; 34862306a36Sopenharmony_ci u32 i; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci block = prestera_counter_block_get_by_idx(counter, counter->curr_idx); 35162306a36Sopenharmony_ci if (!block) { 35262306a36Sopenharmony_ci if (counter->is_fetching) 35362306a36Sopenharmony_ci goto abort; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci goto next; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!counter->is_fetching) { 35962306a36Sopenharmony_ci err = prestera_hw_counter_trigger(counter->sw, block->id); 36062306a36Sopenharmony_ci if (err) 36162306a36Sopenharmony_ci goto abort; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci prestera_counter_block_lock(block); 36462306a36Sopenharmony_ci block->is_updating = true; 36562306a36Sopenharmony_ci prestera_counter_block_unlock(block); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci counter->is_fetching = true; 36862306a36Sopenharmony_ci counter->total_read = 0; 36962306a36Sopenharmony_ci resched_time = COUNTER_RESCHED_TIME; 37062306a36Sopenharmony_ci goto resched; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci prestera_counter_block_lock(block); 37462306a36Sopenharmony_ci err = prestera_hw_counters_get(counter->sw, counter->total_read, 37562306a36Sopenharmony_ci &count, &done, 37662306a36Sopenharmony_ci &block->stats[counter->total_read]); 37762306a36Sopenharmony_ci prestera_counter_block_unlock(block); 37862306a36Sopenharmony_ci if (err) 37962306a36Sopenharmony_ci goto abort; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci counter->total_read += count; 38262306a36Sopenharmony_ci if (!done || counter->total_read < block->num_counters) { 38362306a36Sopenharmony_ci resched_time = COUNTER_RESCHED_TIME; 38462306a36Sopenharmony_ci goto resched; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci for (i = 0; i < block->num_counters; i++) { 38862306a36Sopenharmony_ci if (block->counter_flag[i] == COUNTER_FLAG_INVALID) { 38962306a36Sopenharmony_ci prestera_counter_block_lock(block); 39062306a36Sopenharmony_ci block->counter_flag[i] = COUNTER_FLAG_READY; 39162306a36Sopenharmony_ci memset(&block->stats[i], 0, sizeof(*block->stats)); 39262306a36Sopenharmony_ci prestera_counter_block_unlock(block); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci prestera_counter_block_lock(block); 39762306a36Sopenharmony_ci block->is_updating = false; 39862306a36Sopenharmony_ci prestera_counter_block_unlock(block); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci goto next; 40162306a36Sopenharmony_ciabort: 40262306a36Sopenharmony_ci prestera_hw_counter_abort(counter->sw); 40362306a36Sopenharmony_cinext: 40462306a36Sopenharmony_ci counter->is_fetching = false; 40562306a36Sopenharmony_ci counter->curr_idx = 40662306a36Sopenharmony_ci prestera_counter_block_idx_next(counter, counter->curr_idx); 40762306a36Sopenharmony_ciresched: 40862306a36Sopenharmony_ci if (block) 40962306a36Sopenharmony_ci prestera_counter_block_put(counter, block); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci schedule_delayed_work(&counter->stats_dw, resched_time); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* Can be executed without rtnl_lock(). 41562306a36Sopenharmony_ci * So pay attention when something changing. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ciint prestera_counter_stats_get(struct prestera_counter *counter, 41862306a36Sopenharmony_ci struct prestera_counter_block *block, 41962306a36Sopenharmony_ci u32 counter_id, u64 *packets, u64 *bytes) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci if (!block || !prestera_counter_is_ready(block, counter_id)) { 42262306a36Sopenharmony_ci *packets = 0; 42362306a36Sopenharmony_ci *bytes = 0; 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci prestera_counter_block_lock(block); 42862306a36Sopenharmony_ci *packets = block->stats[counter_id - block->offset].packets; 42962306a36Sopenharmony_ci *bytes = block->stats[counter_id - block->offset].bytes; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci prestera_counter_stats_clear(block, counter_id); 43262306a36Sopenharmony_ci prestera_counter_block_unlock(block); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ciint prestera_counter_init(struct prestera_switch *sw) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct prestera_counter *counter; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci counter = kzalloc(sizeof(*counter), GFP_KERNEL); 44262306a36Sopenharmony_ci if (!counter) 44362306a36Sopenharmony_ci return -ENOMEM; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci counter->block_list = kzalloc(sizeof(*counter->block_list), GFP_KERNEL); 44662306a36Sopenharmony_ci if (!counter->block_list) { 44762306a36Sopenharmony_ci kfree(counter); 44862306a36Sopenharmony_ci return -ENOMEM; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci mutex_init(&counter->mtx); 45262306a36Sopenharmony_ci counter->block_list_len = 1; 45362306a36Sopenharmony_ci counter->sw = sw; 45462306a36Sopenharmony_ci sw->counter = counter; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci INIT_DELAYED_WORK(&counter->stats_dw, prestera_counter_stats_work); 45762306a36Sopenharmony_ci schedule_delayed_work(&counter->stats_dw, COUNTER_POLL_TIME); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return 0; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_civoid prestera_counter_fini(struct prestera_switch *sw) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct prestera_counter *counter = sw->counter; 46562306a36Sopenharmony_ci u32 i; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci cancel_delayed_work_sync(&counter->stats_dw); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci for (i = 0; i < counter->block_list_len; i++) 47062306a36Sopenharmony_ci WARN_ON(counter->block_list[i]); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci mutex_destroy(&counter->mtx); 47362306a36Sopenharmony_ci kfree(counter->block_list); 47462306a36Sopenharmony_ci kfree(counter); 47562306a36Sopenharmony_ci} 476