xref: /openbmc/linux/drivers/net/ethernet/marvell/prestera/prestera_counter.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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