1*6e36c7bcSVolodymyr Mytnyk // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*6e36c7bcSVolodymyr Mytnyk /* Copyright (c) 2021 Marvell International Ltd. All rights reserved */
3*6e36c7bcSVolodymyr Mytnyk
4*6e36c7bcSVolodymyr Mytnyk #include "prestera.h"
5*6e36c7bcSVolodymyr Mytnyk #include "prestera_hw.h"
6*6e36c7bcSVolodymyr Mytnyk #include "prestera_acl.h"
7*6e36c7bcSVolodymyr Mytnyk #include "prestera_counter.h"
8*6e36c7bcSVolodymyr Mytnyk
9*6e36c7bcSVolodymyr Mytnyk #define COUNTER_POLL_TIME (msecs_to_jiffies(1000))
10*6e36c7bcSVolodymyr Mytnyk #define COUNTER_RESCHED_TIME (msecs_to_jiffies(50))
11*6e36c7bcSVolodymyr Mytnyk #define COUNTER_BULK_SIZE (256)
12*6e36c7bcSVolodymyr Mytnyk
13*6e36c7bcSVolodymyr Mytnyk struct prestera_counter {
14*6e36c7bcSVolodymyr Mytnyk struct prestera_switch *sw;
15*6e36c7bcSVolodymyr Mytnyk struct delayed_work stats_dw;
16*6e36c7bcSVolodymyr Mytnyk struct mutex mtx; /* protect block_list */
17*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block **block_list;
18*6e36c7bcSVolodymyr Mytnyk u32 total_read;
19*6e36c7bcSVolodymyr Mytnyk u32 block_list_len;
20*6e36c7bcSVolodymyr Mytnyk u32 curr_idx;
21*6e36c7bcSVolodymyr Mytnyk bool is_fetching;
22*6e36c7bcSVolodymyr Mytnyk };
23*6e36c7bcSVolodymyr Mytnyk
24*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block {
25*6e36c7bcSVolodymyr Mytnyk struct list_head list;
26*6e36c7bcSVolodymyr Mytnyk u32 id;
27*6e36c7bcSVolodymyr Mytnyk u32 offset;
28*6e36c7bcSVolodymyr Mytnyk u32 num_counters;
29*6e36c7bcSVolodymyr Mytnyk u32 client;
30*6e36c7bcSVolodymyr Mytnyk struct idr counter_idr;
31*6e36c7bcSVolodymyr Mytnyk refcount_t refcnt;
32*6e36c7bcSVolodymyr Mytnyk struct mutex mtx; /* protect stats and counter_idr */
33*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_stats *stats;
34*6e36c7bcSVolodymyr Mytnyk u8 *counter_flag;
35*6e36c7bcSVolodymyr Mytnyk bool is_updating;
36*6e36c7bcSVolodymyr Mytnyk bool full;
37*6e36c7bcSVolodymyr Mytnyk };
38*6e36c7bcSVolodymyr Mytnyk
39*6e36c7bcSVolodymyr Mytnyk enum {
40*6e36c7bcSVolodymyr Mytnyk COUNTER_FLAG_READY = 0,
41*6e36c7bcSVolodymyr Mytnyk COUNTER_FLAG_INVALID = 1
42*6e36c7bcSVolodymyr Mytnyk };
43*6e36c7bcSVolodymyr Mytnyk
44*6e36c7bcSVolodymyr Mytnyk static bool
prestera_counter_is_ready(struct prestera_counter_block * block,u32 id)45*6e36c7bcSVolodymyr Mytnyk prestera_counter_is_ready(struct prestera_counter_block *block, u32 id)
46*6e36c7bcSVolodymyr Mytnyk {
47*6e36c7bcSVolodymyr Mytnyk return block->counter_flag[id - block->offset] == COUNTER_FLAG_READY;
48*6e36c7bcSVolodymyr Mytnyk }
49*6e36c7bcSVolodymyr Mytnyk
prestera_counter_lock(struct prestera_counter * counter)50*6e36c7bcSVolodymyr Mytnyk static void prestera_counter_lock(struct prestera_counter *counter)
51*6e36c7bcSVolodymyr Mytnyk {
52*6e36c7bcSVolodymyr Mytnyk mutex_lock(&counter->mtx);
53*6e36c7bcSVolodymyr Mytnyk }
54*6e36c7bcSVolodymyr Mytnyk
prestera_counter_unlock(struct prestera_counter * counter)55*6e36c7bcSVolodymyr Mytnyk static void prestera_counter_unlock(struct prestera_counter *counter)
56*6e36c7bcSVolodymyr Mytnyk {
57*6e36c7bcSVolodymyr Mytnyk mutex_unlock(&counter->mtx);
58*6e36c7bcSVolodymyr Mytnyk }
59*6e36c7bcSVolodymyr Mytnyk
prestera_counter_block_lock(struct prestera_counter_block * block)60*6e36c7bcSVolodymyr Mytnyk static void prestera_counter_block_lock(struct prestera_counter_block *block)
61*6e36c7bcSVolodymyr Mytnyk {
62*6e36c7bcSVolodymyr Mytnyk mutex_lock(&block->mtx);
63*6e36c7bcSVolodymyr Mytnyk }
64*6e36c7bcSVolodymyr Mytnyk
prestera_counter_block_unlock(struct prestera_counter_block * block)65*6e36c7bcSVolodymyr Mytnyk static void prestera_counter_block_unlock(struct prestera_counter_block *block)
66*6e36c7bcSVolodymyr Mytnyk {
67*6e36c7bcSVolodymyr Mytnyk mutex_unlock(&block->mtx);
68*6e36c7bcSVolodymyr Mytnyk }
69*6e36c7bcSVolodymyr Mytnyk
prestera_counter_block_incref(struct prestera_counter_block * block)70*6e36c7bcSVolodymyr Mytnyk static bool prestera_counter_block_incref(struct prestera_counter_block *block)
71*6e36c7bcSVolodymyr Mytnyk {
72*6e36c7bcSVolodymyr Mytnyk return refcount_inc_not_zero(&block->refcnt);
73*6e36c7bcSVolodymyr Mytnyk }
74*6e36c7bcSVolodymyr Mytnyk
prestera_counter_block_decref(struct prestera_counter_block * block)75*6e36c7bcSVolodymyr Mytnyk static bool prestera_counter_block_decref(struct prestera_counter_block *block)
76*6e36c7bcSVolodymyr Mytnyk {
77*6e36c7bcSVolodymyr Mytnyk return refcount_dec_and_test(&block->refcnt);
78*6e36c7bcSVolodymyr Mytnyk }
79*6e36c7bcSVolodymyr Mytnyk
80*6e36c7bcSVolodymyr Mytnyk /* must be called with prestera_counter_block_lock() */
prestera_counter_stats_clear(struct prestera_counter_block * block,u32 counter_id)81*6e36c7bcSVolodymyr Mytnyk static void prestera_counter_stats_clear(struct prestera_counter_block *block,
82*6e36c7bcSVolodymyr Mytnyk u32 counter_id)
83*6e36c7bcSVolodymyr Mytnyk {
84*6e36c7bcSVolodymyr Mytnyk memset(&block->stats[counter_id - block->offset], 0,
85*6e36c7bcSVolodymyr Mytnyk sizeof(*block->stats));
86*6e36c7bcSVolodymyr Mytnyk }
87*6e36c7bcSVolodymyr Mytnyk
88*6e36c7bcSVolodymyr Mytnyk static struct prestera_counter_block *
prestera_counter_block_lookup_not_full(struct prestera_counter * counter,u32 client)89*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_lookup_not_full(struct prestera_counter *counter,
90*6e36c7bcSVolodymyr Mytnyk u32 client)
91*6e36c7bcSVolodymyr Mytnyk {
92*6e36c7bcSVolodymyr Mytnyk u32 i;
93*6e36c7bcSVolodymyr Mytnyk
94*6e36c7bcSVolodymyr Mytnyk prestera_counter_lock(counter);
95*6e36c7bcSVolodymyr Mytnyk for (i = 0; i < counter->block_list_len; i++) {
96*6e36c7bcSVolodymyr Mytnyk if (counter->block_list[i] &&
97*6e36c7bcSVolodymyr Mytnyk counter->block_list[i]->client == client &&
98*6e36c7bcSVolodymyr Mytnyk !counter->block_list[i]->full &&
99*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_incref(counter->block_list[i])) {
100*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
101*6e36c7bcSVolodymyr Mytnyk return counter->block_list[i];
102*6e36c7bcSVolodymyr Mytnyk }
103*6e36c7bcSVolodymyr Mytnyk }
104*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
105*6e36c7bcSVolodymyr Mytnyk
106*6e36c7bcSVolodymyr Mytnyk return NULL;
107*6e36c7bcSVolodymyr Mytnyk }
108*6e36c7bcSVolodymyr Mytnyk
prestera_counter_block_list_add(struct prestera_counter * counter,struct prestera_counter_block * block)109*6e36c7bcSVolodymyr Mytnyk static int prestera_counter_block_list_add(struct prestera_counter *counter,
110*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block *block)
111*6e36c7bcSVolodymyr Mytnyk {
112*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block **arr;
113*6e36c7bcSVolodymyr Mytnyk u32 i;
114*6e36c7bcSVolodymyr Mytnyk
115*6e36c7bcSVolodymyr Mytnyk prestera_counter_lock(counter);
116*6e36c7bcSVolodymyr Mytnyk
117*6e36c7bcSVolodymyr Mytnyk for (i = 0; i < counter->block_list_len; i++) {
118*6e36c7bcSVolodymyr Mytnyk if (counter->block_list[i])
119*6e36c7bcSVolodymyr Mytnyk continue;
120*6e36c7bcSVolodymyr Mytnyk
121*6e36c7bcSVolodymyr Mytnyk counter->block_list[i] = block;
122*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
123*6e36c7bcSVolodymyr Mytnyk return 0;
124*6e36c7bcSVolodymyr Mytnyk }
125*6e36c7bcSVolodymyr Mytnyk
126*6e36c7bcSVolodymyr Mytnyk arr = krealloc(counter->block_list, (counter->block_list_len + 1) *
127*6e36c7bcSVolodymyr Mytnyk sizeof(*counter->block_list), GFP_KERNEL);
128*6e36c7bcSVolodymyr Mytnyk if (!arr) {
129*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
130*6e36c7bcSVolodymyr Mytnyk return -ENOMEM;
131*6e36c7bcSVolodymyr Mytnyk }
132*6e36c7bcSVolodymyr Mytnyk
133*6e36c7bcSVolodymyr Mytnyk counter->block_list = arr;
134*6e36c7bcSVolodymyr Mytnyk counter->block_list[counter->block_list_len] = block;
135*6e36c7bcSVolodymyr Mytnyk counter->block_list_len++;
136*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
137*6e36c7bcSVolodymyr Mytnyk return 0;
138*6e36c7bcSVolodymyr Mytnyk }
139*6e36c7bcSVolodymyr Mytnyk
140*6e36c7bcSVolodymyr Mytnyk static struct prestera_counter_block *
prestera_counter_block_get(struct prestera_counter * counter,u32 client)141*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_get(struct prestera_counter *counter, u32 client)
142*6e36c7bcSVolodymyr Mytnyk {
143*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block *block;
144*6e36c7bcSVolodymyr Mytnyk int err;
145*6e36c7bcSVolodymyr Mytnyk
146*6e36c7bcSVolodymyr Mytnyk block = prestera_counter_block_lookup_not_full(counter, client);
147*6e36c7bcSVolodymyr Mytnyk if (block)
148*6e36c7bcSVolodymyr Mytnyk return block;
149*6e36c7bcSVolodymyr Mytnyk
150*6e36c7bcSVolodymyr Mytnyk block = kzalloc(sizeof(*block), GFP_KERNEL);
151*6e36c7bcSVolodymyr Mytnyk if (!block)
152*6e36c7bcSVolodymyr Mytnyk return ERR_PTR(-ENOMEM);
153*6e36c7bcSVolodymyr Mytnyk
154*6e36c7bcSVolodymyr Mytnyk err = prestera_hw_counter_block_get(counter->sw, client,
155*6e36c7bcSVolodymyr Mytnyk &block->id, &block->offset,
156*6e36c7bcSVolodymyr Mytnyk &block->num_counters);
157*6e36c7bcSVolodymyr Mytnyk if (err)
158*6e36c7bcSVolodymyr Mytnyk goto err_block;
159*6e36c7bcSVolodymyr Mytnyk
160*6e36c7bcSVolodymyr Mytnyk block->stats = kcalloc(block->num_counters,
161*6e36c7bcSVolodymyr Mytnyk sizeof(*block->stats), GFP_KERNEL);
162*6e36c7bcSVolodymyr Mytnyk if (!block->stats) {
163*6e36c7bcSVolodymyr Mytnyk err = -ENOMEM;
164*6e36c7bcSVolodymyr Mytnyk goto err_stats;
165*6e36c7bcSVolodymyr Mytnyk }
166*6e36c7bcSVolodymyr Mytnyk
167*6e36c7bcSVolodymyr Mytnyk block->counter_flag = kcalloc(block->num_counters,
168*6e36c7bcSVolodymyr Mytnyk sizeof(*block->counter_flag),
169*6e36c7bcSVolodymyr Mytnyk GFP_KERNEL);
170*6e36c7bcSVolodymyr Mytnyk if (!block->counter_flag) {
171*6e36c7bcSVolodymyr Mytnyk err = -ENOMEM;
172*6e36c7bcSVolodymyr Mytnyk goto err_flag;
173*6e36c7bcSVolodymyr Mytnyk }
174*6e36c7bcSVolodymyr Mytnyk
175*6e36c7bcSVolodymyr Mytnyk block->client = client;
176*6e36c7bcSVolodymyr Mytnyk mutex_init(&block->mtx);
177*6e36c7bcSVolodymyr Mytnyk refcount_set(&block->refcnt, 1);
178*6e36c7bcSVolodymyr Mytnyk idr_init_base(&block->counter_idr, block->offset);
179*6e36c7bcSVolodymyr Mytnyk
180*6e36c7bcSVolodymyr Mytnyk err = prestera_counter_block_list_add(counter, block);
181*6e36c7bcSVolodymyr Mytnyk if (err)
182*6e36c7bcSVolodymyr Mytnyk goto err_list_add;
183*6e36c7bcSVolodymyr Mytnyk
184*6e36c7bcSVolodymyr Mytnyk return block;
185*6e36c7bcSVolodymyr Mytnyk
186*6e36c7bcSVolodymyr Mytnyk err_list_add:
187*6e36c7bcSVolodymyr Mytnyk idr_destroy(&block->counter_idr);
188*6e36c7bcSVolodymyr Mytnyk mutex_destroy(&block->mtx);
189*6e36c7bcSVolodymyr Mytnyk kfree(block->counter_flag);
190*6e36c7bcSVolodymyr Mytnyk err_flag:
191*6e36c7bcSVolodymyr Mytnyk kfree(block->stats);
192*6e36c7bcSVolodymyr Mytnyk err_stats:
193*6e36c7bcSVolodymyr Mytnyk prestera_hw_counter_block_release(counter->sw, block->id);
194*6e36c7bcSVolodymyr Mytnyk err_block:
195*6e36c7bcSVolodymyr Mytnyk kfree(block);
196*6e36c7bcSVolodymyr Mytnyk return ERR_PTR(err);
197*6e36c7bcSVolodymyr Mytnyk }
198*6e36c7bcSVolodymyr Mytnyk
prestera_counter_block_put(struct prestera_counter * counter,struct prestera_counter_block * block)199*6e36c7bcSVolodymyr Mytnyk static void prestera_counter_block_put(struct prestera_counter *counter,
200*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block *block)
201*6e36c7bcSVolodymyr Mytnyk {
202*6e36c7bcSVolodymyr Mytnyk u32 i;
203*6e36c7bcSVolodymyr Mytnyk
204*6e36c7bcSVolodymyr Mytnyk if (!prestera_counter_block_decref(block))
205*6e36c7bcSVolodymyr Mytnyk return;
206*6e36c7bcSVolodymyr Mytnyk
207*6e36c7bcSVolodymyr Mytnyk prestera_counter_lock(counter);
208*6e36c7bcSVolodymyr Mytnyk for (i = 0; i < counter->block_list_len; i++) {
209*6e36c7bcSVolodymyr Mytnyk if (counter->block_list[i] &&
210*6e36c7bcSVolodymyr Mytnyk counter->block_list[i]->id == block->id) {
211*6e36c7bcSVolodymyr Mytnyk counter->block_list[i] = NULL;
212*6e36c7bcSVolodymyr Mytnyk break;
213*6e36c7bcSVolodymyr Mytnyk }
214*6e36c7bcSVolodymyr Mytnyk }
215*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
216*6e36c7bcSVolodymyr Mytnyk
217*6e36c7bcSVolodymyr Mytnyk WARN_ON(!idr_is_empty(&block->counter_idr));
218*6e36c7bcSVolodymyr Mytnyk
219*6e36c7bcSVolodymyr Mytnyk prestera_hw_counter_block_release(counter->sw, block->id);
220*6e36c7bcSVolodymyr Mytnyk idr_destroy(&block->counter_idr);
221*6e36c7bcSVolodymyr Mytnyk mutex_destroy(&block->mtx);
222*6e36c7bcSVolodymyr Mytnyk kfree(block->stats);
223*6e36c7bcSVolodymyr Mytnyk kfree(block);
224*6e36c7bcSVolodymyr Mytnyk }
225*6e36c7bcSVolodymyr Mytnyk
prestera_counter_get_vacant(struct prestera_counter_block * block,u32 * id)226*6e36c7bcSVolodymyr Mytnyk static int prestera_counter_get_vacant(struct prestera_counter_block *block,
227*6e36c7bcSVolodymyr Mytnyk u32 *id)
228*6e36c7bcSVolodymyr Mytnyk {
229*6e36c7bcSVolodymyr Mytnyk int free_id;
230*6e36c7bcSVolodymyr Mytnyk
231*6e36c7bcSVolodymyr Mytnyk if (block->full)
232*6e36c7bcSVolodymyr Mytnyk return -ENOSPC;
233*6e36c7bcSVolodymyr Mytnyk
234*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_lock(block);
235*6e36c7bcSVolodymyr Mytnyk free_id = idr_alloc_cyclic(&block->counter_idr, NULL, block->offset,
236*6e36c7bcSVolodymyr Mytnyk block->offset + block->num_counters,
237*6e36c7bcSVolodymyr Mytnyk GFP_KERNEL);
238*6e36c7bcSVolodymyr Mytnyk if (free_id < 0) {
239*6e36c7bcSVolodymyr Mytnyk if (free_id == -ENOSPC)
240*6e36c7bcSVolodymyr Mytnyk block->full = true;
241*6e36c7bcSVolodymyr Mytnyk
242*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_unlock(block);
243*6e36c7bcSVolodymyr Mytnyk return free_id;
244*6e36c7bcSVolodymyr Mytnyk }
245*6e36c7bcSVolodymyr Mytnyk *id = free_id;
246*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_unlock(block);
247*6e36c7bcSVolodymyr Mytnyk
248*6e36c7bcSVolodymyr Mytnyk return 0;
249*6e36c7bcSVolodymyr Mytnyk }
250*6e36c7bcSVolodymyr Mytnyk
prestera_counter_get(struct prestera_counter * counter,u32 client,struct prestera_counter_block ** bl,u32 * counter_id)251*6e36c7bcSVolodymyr Mytnyk int prestera_counter_get(struct prestera_counter *counter, u32 client,
252*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block **bl, u32 *counter_id)
253*6e36c7bcSVolodymyr Mytnyk {
254*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block *block;
255*6e36c7bcSVolodymyr Mytnyk int err;
256*6e36c7bcSVolodymyr Mytnyk u32 id;
257*6e36c7bcSVolodymyr Mytnyk
258*6e36c7bcSVolodymyr Mytnyk get_next_block:
259*6e36c7bcSVolodymyr Mytnyk block = prestera_counter_block_get(counter, client);
260*6e36c7bcSVolodymyr Mytnyk if (IS_ERR(block))
261*6e36c7bcSVolodymyr Mytnyk return PTR_ERR(block);
262*6e36c7bcSVolodymyr Mytnyk
263*6e36c7bcSVolodymyr Mytnyk err = prestera_counter_get_vacant(block, &id);
264*6e36c7bcSVolodymyr Mytnyk if (err) {
265*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_put(counter, block);
266*6e36c7bcSVolodymyr Mytnyk
267*6e36c7bcSVolodymyr Mytnyk if (err == -ENOSPC)
268*6e36c7bcSVolodymyr Mytnyk goto get_next_block;
269*6e36c7bcSVolodymyr Mytnyk
270*6e36c7bcSVolodymyr Mytnyk return err;
271*6e36c7bcSVolodymyr Mytnyk }
272*6e36c7bcSVolodymyr Mytnyk
273*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_lock(block);
274*6e36c7bcSVolodymyr Mytnyk if (block->is_updating)
275*6e36c7bcSVolodymyr Mytnyk block->counter_flag[id - block->offset] = COUNTER_FLAG_INVALID;
276*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_unlock(block);
277*6e36c7bcSVolodymyr Mytnyk
278*6e36c7bcSVolodymyr Mytnyk *counter_id = id;
279*6e36c7bcSVolodymyr Mytnyk *bl = block;
280*6e36c7bcSVolodymyr Mytnyk
281*6e36c7bcSVolodymyr Mytnyk return 0;
282*6e36c7bcSVolodymyr Mytnyk }
283*6e36c7bcSVolodymyr Mytnyk
prestera_counter_put(struct prestera_counter * counter,struct prestera_counter_block * block,u32 counter_id)284*6e36c7bcSVolodymyr Mytnyk void prestera_counter_put(struct prestera_counter *counter,
285*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block *block, u32 counter_id)
286*6e36c7bcSVolodymyr Mytnyk {
287*6e36c7bcSVolodymyr Mytnyk if (!block)
288*6e36c7bcSVolodymyr Mytnyk return;
289*6e36c7bcSVolodymyr Mytnyk
290*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_lock(block);
291*6e36c7bcSVolodymyr Mytnyk idr_remove(&block->counter_idr, counter_id);
292*6e36c7bcSVolodymyr Mytnyk block->full = false;
293*6e36c7bcSVolodymyr Mytnyk prestera_counter_stats_clear(block, counter_id);
294*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_unlock(block);
295*6e36c7bcSVolodymyr Mytnyk
296*6e36c7bcSVolodymyr Mytnyk prestera_hw_counter_clear(counter->sw, block->id, counter_id);
297*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_put(counter, block);
298*6e36c7bcSVolodymyr Mytnyk }
299*6e36c7bcSVolodymyr Mytnyk
prestera_counter_block_idx_next(struct prestera_counter * counter,u32 curr_idx)300*6e36c7bcSVolodymyr Mytnyk static u32 prestera_counter_block_idx_next(struct prestera_counter *counter,
301*6e36c7bcSVolodymyr Mytnyk u32 curr_idx)
302*6e36c7bcSVolodymyr Mytnyk {
303*6e36c7bcSVolodymyr Mytnyk u32 idx, i, start = curr_idx + 1;
304*6e36c7bcSVolodymyr Mytnyk
305*6e36c7bcSVolodymyr Mytnyk prestera_counter_lock(counter);
306*6e36c7bcSVolodymyr Mytnyk for (i = 0; i < counter->block_list_len; i++) {
307*6e36c7bcSVolodymyr Mytnyk idx = (start + i) % counter->block_list_len;
308*6e36c7bcSVolodymyr Mytnyk if (!counter->block_list[idx])
309*6e36c7bcSVolodymyr Mytnyk continue;
310*6e36c7bcSVolodymyr Mytnyk
311*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
312*6e36c7bcSVolodymyr Mytnyk return idx;
313*6e36c7bcSVolodymyr Mytnyk }
314*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
315*6e36c7bcSVolodymyr Mytnyk
316*6e36c7bcSVolodymyr Mytnyk return 0;
317*6e36c7bcSVolodymyr Mytnyk }
318*6e36c7bcSVolodymyr Mytnyk
319*6e36c7bcSVolodymyr Mytnyk static struct prestera_counter_block *
prestera_counter_block_get_by_idx(struct prestera_counter * counter,u32 idx)320*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_get_by_idx(struct prestera_counter *counter, u32 idx)
321*6e36c7bcSVolodymyr Mytnyk {
322*6e36c7bcSVolodymyr Mytnyk if (idx >= counter->block_list_len)
323*6e36c7bcSVolodymyr Mytnyk return NULL;
324*6e36c7bcSVolodymyr Mytnyk
325*6e36c7bcSVolodymyr Mytnyk prestera_counter_lock(counter);
326*6e36c7bcSVolodymyr Mytnyk
327*6e36c7bcSVolodymyr Mytnyk if (!counter->block_list[idx] ||
328*6e36c7bcSVolodymyr Mytnyk !prestera_counter_block_incref(counter->block_list[idx])) {
329*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
330*6e36c7bcSVolodymyr Mytnyk return NULL;
331*6e36c7bcSVolodymyr Mytnyk }
332*6e36c7bcSVolodymyr Mytnyk
333*6e36c7bcSVolodymyr Mytnyk prestera_counter_unlock(counter);
334*6e36c7bcSVolodymyr Mytnyk return counter->block_list[idx];
335*6e36c7bcSVolodymyr Mytnyk }
336*6e36c7bcSVolodymyr Mytnyk
prestera_counter_stats_work(struct work_struct * work)337*6e36c7bcSVolodymyr Mytnyk static void prestera_counter_stats_work(struct work_struct *work)
338*6e36c7bcSVolodymyr Mytnyk {
339*6e36c7bcSVolodymyr Mytnyk struct delayed_work *dl_work =
340*6e36c7bcSVolodymyr Mytnyk container_of(work, struct delayed_work, work);
341*6e36c7bcSVolodymyr Mytnyk struct prestera_counter *counter =
342*6e36c7bcSVolodymyr Mytnyk container_of(dl_work, struct prestera_counter, stats_dw);
343*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block *block;
344*6e36c7bcSVolodymyr Mytnyk u32 resched_time = COUNTER_POLL_TIME;
345*6e36c7bcSVolodymyr Mytnyk u32 count = COUNTER_BULK_SIZE;
346*6e36c7bcSVolodymyr Mytnyk bool done = false;
347*6e36c7bcSVolodymyr Mytnyk int err;
348*6e36c7bcSVolodymyr Mytnyk u32 i;
349*6e36c7bcSVolodymyr Mytnyk
350*6e36c7bcSVolodymyr Mytnyk block = prestera_counter_block_get_by_idx(counter, counter->curr_idx);
351*6e36c7bcSVolodymyr Mytnyk if (!block) {
352*6e36c7bcSVolodymyr Mytnyk if (counter->is_fetching)
353*6e36c7bcSVolodymyr Mytnyk goto abort;
354*6e36c7bcSVolodymyr Mytnyk
355*6e36c7bcSVolodymyr Mytnyk goto next;
356*6e36c7bcSVolodymyr Mytnyk }
357*6e36c7bcSVolodymyr Mytnyk
358*6e36c7bcSVolodymyr Mytnyk if (!counter->is_fetching) {
359*6e36c7bcSVolodymyr Mytnyk err = prestera_hw_counter_trigger(counter->sw, block->id);
360*6e36c7bcSVolodymyr Mytnyk if (err)
361*6e36c7bcSVolodymyr Mytnyk goto abort;
362*6e36c7bcSVolodymyr Mytnyk
363*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_lock(block);
364*6e36c7bcSVolodymyr Mytnyk block->is_updating = true;
365*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_unlock(block);
366*6e36c7bcSVolodymyr Mytnyk
367*6e36c7bcSVolodymyr Mytnyk counter->is_fetching = true;
368*6e36c7bcSVolodymyr Mytnyk counter->total_read = 0;
369*6e36c7bcSVolodymyr Mytnyk resched_time = COUNTER_RESCHED_TIME;
370*6e36c7bcSVolodymyr Mytnyk goto resched;
371*6e36c7bcSVolodymyr Mytnyk }
372*6e36c7bcSVolodymyr Mytnyk
373*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_lock(block);
374*6e36c7bcSVolodymyr Mytnyk err = prestera_hw_counters_get(counter->sw, counter->total_read,
375*6e36c7bcSVolodymyr Mytnyk &count, &done,
376*6e36c7bcSVolodymyr Mytnyk &block->stats[counter->total_read]);
377*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_unlock(block);
378*6e36c7bcSVolodymyr Mytnyk if (err)
379*6e36c7bcSVolodymyr Mytnyk goto abort;
380*6e36c7bcSVolodymyr Mytnyk
381*6e36c7bcSVolodymyr Mytnyk counter->total_read += count;
382*6e36c7bcSVolodymyr Mytnyk if (!done || counter->total_read < block->num_counters) {
383*6e36c7bcSVolodymyr Mytnyk resched_time = COUNTER_RESCHED_TIME;
384*6e36c7bcSVolodymyr Mytnyk goto resched;
385*6e36c7bcSVolodymyr Mytnyk }
386*6e36c7bcSVolodymyr Mytnyk
387*6e36c7bcSVolodymyr Mytnyk for (i = 0; i < block->num_counters; i++) {
388*6e36c7bcSVolodymyr Mytnyk if (block->counter_flag[i] == COUNTER_FLAG_INVALID) {
389*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_lock(block);
390*6e36c7bcSVolodymyr Mytnyk block->counter_flag[i] = COUNTER_FLAG_READY;
391*6e36c7bcSVolodymyr Mytnyk memset(&block->stats[i], 0, sizeof(*block->stats));
392*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_unlock(block);
393*6e36c7bcSVolodymyr Mytnyk }
394*6e36c7bcSVolodymyr Mytnyk }
395*6e36c7bcSVolodymyr Mytnyk
396*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_lock(block);
397*6e36c7bcSVolodymyr Mytnyk block->is_updating = false;
398*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_unlock(block);
399*6e36c7bcSVolodymyr Mytnyk
400*6e36c7bcSVolodymyr Mytnyk goto next;
401*6e36c7bcSVolodymyr Mytnyk abort:
402*6e36c7bcSVolodymyr Mytnyk prestera_hw_counter_abort(counter->sw);
403*6e36c7bcSVolodymyr Mytnyk next:
404*6e36c7bcSVolodymyr Mytnyk counter->is_fetching = false;
405*6e36c7bcSVolodymyr Mytnyk counter->curr_idx =
406*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_idx_next(counter, counter->curr_idx);
407*6e36c7bcSVolodymyr Mytnyk resched:
408*6e36c7bcSVolodymyr Mytnyk if (block)
409*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_put(counter, block);
410*6e36c7bcSVolodymyr Mytnyk
411*6e36c7bcSVolodymyr Mytnyk schedule_delayed_work(&counter->stats_dw, resched_time);
412*6e36c7bcSVolodymyr Mytnyk }
413*6e36c7bcSVolodymyr Mytnyk
414*6e36c7bcSVolodymyr Mytnyk /* Can be executed without rtnl_lock().
415*6e36c7bcSVolodymyr Mytnyk * So pay attention when something changing.
416*6e36c7bcSVolodymyr Mytnyk */
prestera_counter_stats_get(struct prestera_counter * counter,struct prestera_counter_block * block,u32 counter_id,u64 * packets,u64 * bytes)417*6e36c7bcSVolodymyr Mytnyk int prestera_counter_stats_get(struct prestera_counter *counter,
418*6e36c7bcSVolodymyr Mytnyk struct prestera_counter_block *block,
419*6e36c7bcSVolodymyr Mytnyk u32 counter_id, u64 *packets, u64 *bytes)
420*6e36c7bcSVolodymyr Mytnyk {
421*6e36c7bcSVolodymyr Mytnyk if (!block || !prestera_counter_is_ready(block, counter_id)) {
422*6e36c7bcSVolodymyr Mytnyk *packets = 0;
423*6e36c7bcSVolodymyr Mytnyk *bytes = 0;
424*6e36c7bcSVolodymyr Mytnyk return 0;
425*6e36c7bcSVolodymyr Mytnyk }
426*6e36c7bcSVolodymyr Mytnyk
427*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_lock(block);
428*6e36c7bcSVolodymyr Mytnyk *packets = block->stats[counter_id - block->offset].packets;
429*6e36c7bcSVolodymyr Mytnyk *bytes = block->stats[counter_id - block->offset].bytes;
430*6e36c7bcSVolodymyr Mytnyk
431*6e36c7bcSVolodymyr Mytnyk prestera_counter_stats_clear(block, counter_id);
432*6e36c7bcSVolodymyr Mytnyk prestera_counter_block_unlock(block);
433*6e36c7bcSVolodymyr Mytnyk
434*6e36c7bcSVolodymyr Mytnyk return 0;
435*6e36c7bcSVolodymyr Mytnyk }
436*6e36c7bcSVolodymyr Mytnyk
prestera_counter_init(struct prestera_switch * sw)437*6e36c7bcSVolodymyr Mytnyk int prestera_counter_init(struct prestera_switch *sw)
438*6e36c7bcSVolodymyr Mytnyk {
439*6e36c7bcSVolodymyr Mytnyk struct prestera_counter *counter;
440*6e36c7bcSVolodymyr Mytnyk
441*6e36c7bcSVolodymyr Mytnyk counter = kzalloc(sizeof(*counter), GFP_KERNEL);
442*6e36c7bcSVolodymyr Mytnyk if (!counter)
443*6e36c7bcSVolodymyr Mytnyk return -ENOMEM;
444*6e36c7bcSVolodymyr Mytnyk
445*6e36c7bcSVolodymyr Mytnyk counter->block_list = kzalloc(sizeof(*counter->block_list), GFP_KERNEL);
446*6e36c7bcSVolodymyr Mytnyk if (!counter->block_list) {
447*6e36c7bcSVolodymyr Mytnyk kfree(counter);
448*6e36c7bcSVolodymyr Mytnyk return -ENOMEM;
449*6e36c7bcSVolodymyr Mytnyk }
450*6e36c7bcSVolodymyr Mytnyk
451*6e36c7bcSVolodymyr Mytnyk mutex_init(&counter->mtx);
452*6e36c7bcSVolodymyr Mytnyk counter->block_list_len = 1;
453*6e36c7bcSVolodymyr Mytnyk counter->sw = sw;
454*6e36c7bcSVolodymyr Mytnyk sw->counter = counter;
455*6e36c7bcSVolodymyr Mytnyk
456*6e36c7bcSVolodymyr Mytnyk INIT_DELAYED_WORK(&counter->stats_dw, prestera_counter_stats_work);
457*6e36c7bcSVolodymyr Mytnyk schedule_delayed_work(&counter->stats_dw, COUNTER_POLL_TIME);
458*6e36c7bcSVolodymyr Mytnyk
459*6e36c7bcSVolodymyr Mytnyk return 0;
460*6e36c7bcSVolodymyr Mytnyk }
461*6e36c7bcSVolodymyr Mytnyk
prestera_counter_fini(struct prestera_switch * sw)462*6e36c7bcSVolodymyr Mytnyk void prestera_counter_fini(struct prestera_switch *sw)
463*6e36c7bcSVolodymyr Mytnyk {
464*6e36c7bcSVolodymyr Mytnyk struct prestera_counter *counter = sw->counter;
465*6e36c7bcSVolodymyr Mytnyk u32 i;
466*6e36c7bcSVolodymyr Mytnyk
467*6e36c7bcSVolodymyr Mytnyk cancel_delayed_work_sync(&counter->stats_dw);
468*6e36c7bcSVolodymyr Mytnyk
469*6e36c7bcSVolodymyr Mytnyk for (i = 0; i < counter->block_list_len; i++)
470*6e36c7bcSVolodymyr Mytnyk WARN_ON(counter->block_list[i]);
471*6e36c7bcSVolodymyr Mytnyk
472*6e36c7bcSVolodymyr Mytnyk mutex_destroy(&counter->mtx);
473*6e36c7bcSVolodymyr Mytnyk kfree(counter->block_list);
474*6e36c7bcSVolodymyr Mytnyk kfree(counter);
475*6e36c7bcSVolodymyr Mytnyk }
476