Commit | Line | Data |
---|---|---|
9948a064 JP |
1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ | |
ff7b0d27 AS |
3 | |
4 | #include <linux/kernel.h> | |
5 | #include <linux/bitops.h> | |
6c5a688e | 6 | #include <linux/spinlock.h> |
ff7b0d27 AS |
7 | |
8 | #include "spectrum_cnt.h" | |
9 | ||
ff7b0d27 | 10 | struct mlxsw_sp_counter_sub_pool { |
d53cdbb8 | 11 | u64 size; |
ff7b0d27 | 12 | unsigned int base_index; |
b2d3e33c | 13 | enum mlxsw_res_id entry_size_res_id; |
d53cdbb8 JP |
14 | const char *resource_name; /* devlink resource name */ |
15 | u64 resource_id; /* devlink resource id */ | |
ff7b0d27 AS |
16 | unsigned int entry_size; |
17 | unsigned int bank_count; | |
4e145fc6 | 18 | atomic_t active_entries_count; |
ff7b0d27 AS |
19 | }; |
20 | ||
21 | struct mlxsw_sp_counter_pool { | |
d53cdbb8 | 22 | u64 pool_size; |
ff7b0d27 | 23 | unsigned long *usage; /* Usage bitmap */ |
6c5a688e | 24 | spinlock_t counter_pool_lock; /* Protects counter pool allocations */ |
4e145fc6 | 25 | atomic_t active_entries_count; |
c33fbe94 JP |
26 | unsigned int sub_pools_count; |
27 | struct mlxsw_sp_counter_sub_pool sub_pools[]; | |
ff7b0d27 AS |
28 | }; |
29 | ||
c33fbe94 | 30 | static const struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = { |
1abcbcc2 | 31 | [MLXSW_SP_COUNTER_SUB_POOL_FLOW] = { |
b2d3e33c | 32 | .entry_size_res_id = MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES, |
d53cdbb8 JP |
33 | .resource_name = MLXSW_SP_RESOURCE_NAME_COUNTERS_FLOW, |
34 | .resource_id = MLXSW_SP_RESOURCE_COUNTERS_FLOW, | |
1abcbcc2 AS |
35 | .bank_count = 6, |
36 | }, | |
e0c0afd8 | 37 | [MLXSW_SP_COUNTER_SUB_POOL_RIF] = { |
b2d3e33c | 38 | .entry_size_res_id = MLXSW_RES_ID_COUNTER_SIZE_ROUTER_BASIC, |
d53cdbb8 JP |
39 | .resource_name = MLXSW_SP_RESOURCE_NAME_COUNTERS_RIF, |
40 | .resource_id = MLXSW_SP_RESOURCE_COUNTERS_RIF, | |
e0c0afd8 AS |
41 | .bank_count = 2, |
42 | } | |
1abcbcc2 | 43 | }; |
ff7b0d27 | 44 | |
4e145fc6 JP |
45 | static u64 mlxsw_sp_counter_sub_pool_occ_get(void *priv) |
46 | { | |
47 | const struct mlxsw_sp_counter_sub_pool *sub_pool = priv; | |
48 | ||
49 | return atomic_read(&sub_pool->active_entries_count); | |
50 | } | |
51 | ||
53d96366 | 52 | static int mlxsw_sp_counter_sub_pools_init(struct mlxsw_sp *mlxsw_sp) |
1abcbcc2 | 53 | { |
c33fbe94 | 54 | struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; |
53d96366 | 55 | struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); |
1abcbcc2 | 56 | struct mlxsw_sp_counter_sub_pool *sub_pool; |
53d96366 | 57 | unsigned int base_index = 0; |
b2d3e33c | 58 | enum mlxsw_res_id res_id; |
53d96366 | 59 | int err; |
b2d3e33c | 60 | int i; |
1abcbcc2 | 61 | |
b2d3e33c JP |
62 | for (i = 0; i < pool->sub_pools_count; i++) { |
63 | sub_pool = &pool->sub_pools[i]; | |
64 | res_id = sub_pool->entry_size_res_id; | |
65 | ||
66 | if (!mlxsw_core_res_valid(mlxsw_sp->core, res_id)) | |
67 | return -EIO; | |
68 | sub_pool->entry_size = mlxsw_core_res_get(mlxsw_sp->core, | |
69 | res_id); | |
72a4c8c9 JP |
70 | err = devl_resource_size_get(devlink, |
71 | sub_pool->resource_id, | |
72 | &sub_pool->size); | |
53d96366 | 73 | if (err) |
4e145fc6 JP |
74 | goto err_resource_size_get; |
75 | ||
72a4c8c9 JP |
76 | devl_resource_occ_get_register(devlink, |
77 | sub_pool->resource_id, | |
78 | mlxsw_sp_counter_sub_pool_occ_get, | |
79 | sub_pool); | |
53d96366 JP |
80 | |
81 | sub_pool->base_index = base_index; | |
82 | base_index += sub_pool->size; | |
4e145fc6 | 83 | atomic_set(&sub_pool->active_entries_count, 0); |
b2d3e33c | 84 | } |
1abcbcc2 | 85 | return 0; |
4e145fc6 JP |
86 | |
87 | err_resource_size_get: | |
88 | for (i--; i >= 0; i--) { | |
89 | sub_pool = &pool->sub_pools[i]; | |
90 | ||
72a4c8c9 JP |
91 | devl_resource_occ_get_unregister(devlink, |
92 | sub_pool->resource_id); | |
4e145fc6 JP |
93 | } |
94 | return err; | |
95 | } | |
96 | ||
97 | static void mlxsw_sp_counter_sub_pools_fini(struct mlxsw_sp *mlxsw_sp) | |
98 | { | |
99 | struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; | |
100 | struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); | |
101 | struct mlxsw_sp_counter_sub_pool *sub_pool; | |
102 | int i; | |
103 | ||
104 | for (i = 0; i < pool->sub_pools_count; i++) { | |
105 | sub_pool = &pool->sub_pools[i]; | |
106 | ||
107 | WARN_ON(atomic_read(&sub_pool->active_entries_count)); | |
72a4c8c9 JP |
108 | devl_resource_occ_get_unregister(devlink, |
109 | sub_pool->resource_id); | |
4e145fc6 JP |
110 | } |
111 | } | |
112 | ||
113 | static u64 mlxsw_sp_counter_pool_occ_get(void *priv) | |
114 | { | |
115 | const struct mlxsw_sp_counter_pool *pool = priv; | |
116 | ||
117 | return atomic_read(&pool->active_entries_count); | |
1abcbcc2 AS |
118 | } |
119 | ||
ff7b0d27 AS |
120 | int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp) |
121 | { | |
c33fbe94 | 122 | unsigned int sub_pools_count = ARRAY_SIZE(mlxsw_sp_counter_sub_pools); |
d53cdbb8 | 123 | struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); |
ff7b0d27 | 124 | struct mlxsw_sp_counter_pool *pool; |
ff7b0d27 AS |
125 | int err; |
126 | ||
c33fbe94 JP |
127 | pool = kzalloc(struct_size(pool, sub_pools, sub_pools_count), |
128 | GFP_KERNEL); | |
129 | if (!pool) | |
130 | return -ENOMEM; | |
131 | mlxsw_sp->counter_pool = pool; | |
c33fbe94 | 132 | pool->sub_pools_count = sub_pools_count; |
10470c0d GS |
133 | memcpy(pool->sub_pools, mlxsw_sp_counter_sub_pools, |
134 | flex_array_size(pool, sub_pools, pool->sub_pools_count)); | |
c33fbe94 | 135 | spin_lock_init(&pool->counter_pool_lock); |
4e145fc6 | 136 | atomic_set(&pool->active_entries_count, 0); |
c33fbe94 | 137 | |
72a4c8c9 JP |
138 | err = devl_resource_size_get(devlink, MLXSW_SP_RESOURCE_COUNTERS, |
139 | &pool->pool_size); | |
d53cdbb8 JP |
140 | if (err) |
141 | goto err_pool_resource_size_get; | |
72a4c8c9 JP |
142 | devl_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_COUNTERS, |
143 | mlxsw_sp_counter_pool_occ_get, pool); | |
ff7b0d27 | 144 | |
2c087dfc | 145 | pool->usage = bitmap_zalloc(pool->pool_size, GFP_KERNEL); |
ff7b0d27 AS |
146 | if (!pool->usage) { |
147 | err = -ENOMEM; | |
148 | goto err_usage_alloc; | |
149 | } | |
150 | ||
53d96366 JP |
151 | err = mlxsw_sp_counter_sub_pools_init(mlxsw_sp); |
152 | if (err) | |
153 | goto err_sub_pools_init; | |
ff7b0d27 | 154 | |
ff7b0d27 AS |
155 | return 0; |
156 | ||
53d96366 | 157 | err_sub_pools_init: |
2c087dfc | 158 | bitmap_free(pool->usage); |
ff7b0d27 | 159 | err_usage_alloc: |
72a4c8c9 JP |
160 | devl_resource_occ_get_unregister(devlink, |
161 | MLXSW_SP_RESOURCE_COUNTERS); | |
d53cdbb8 | 162 | err_pool_resource_size_get: |
ff7b0d27 AS |
163 | kfree(pool); |
164 | return err; | |
165 | } | |
166 | ||
167 | void mlxsw_sp_counter_pool_fini(struct mlxsw_sp *mlxsw_sp) | |
168 | { | |
169 | struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; | |
4e145fc6 | 170 | struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); |
ff7b0d27 | 171 | |
4e145fc6 | 172 | mlxsw_sp_counter_sub_pools_fini(mlxsw_sp); |
ff7b0d27 AS |
173 | WARN_ON(find_first_bit(pool->usage, pool->pool_size) != |
174 | pool->pool_size); | |
4e145fc6 | 175 | WARN_ON(atomic_read(&pool->active_entries_count)); |
2c087dfc | 176 | bitmap_free(pool->usage); |
72a4c8c9 JP |
177 | devl_resource_occ_get_unregister(devlink, |
178 | MLXSW_SP_RESOURCE_COUNTERS); | |
ff7b0d27 AS |
179 | kfree(pool); |
180 | } | |
181 | ||
182 | int mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp, | |
183 | enum mlxsw_sp_counter_sub_pool_id sub_pool_id, | |
184 | unsigned int *p_counter_index) | |
185 | { | |
186 | struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; | |
187 | struct mlxsw_sp_counter_sub_pool *sub_pool; | |
188 | unsigned int entry_index; | |
189 | unsigned int stop_index; | |
6c5a688e | 190 | int i, err; |
ff7b0d27 | 191 | |
c33fbe94 | 192 | sub_pool = &pool->sub_pools[sub_pool_id]; |
ff7b0d27 AS |
193 | stop_index = sub_pool->base_index + sub_pool->size; |
194 | entry_index = sub_pool->base_index; | |
195 | ||
6c5a688e | 196 | spin_lock(&pool->counter_pool_lock); |
ff7b0d27 | 197 | entry_index = find_next_zero_bit(pool->usage, stop_index, entry_index); |
6c5a688e IS |
198 | if (entry_index == stop_index) { |
199 | err = -ENOBUFS; | |
200 | goto err_alloc; | |
201 | } | |
ff7b0d27 AS |
202 | /* The sub-pools can contain non-integer number of entries |
203 | * so we must check for overflow | |
204 | */ | |
6c5a688e IS |
205 | if (entry_index + sub_pool->entry_size > stop_index) { |
206 | err = -ENOBUFS; | |
207 | goto err_alloc; | |
208 | } | |
ff7b0d27 AS |
209 | for (i = 0; i < sub_pool->entry_size; i++) |
210 | __set_bit(entry_index + i, pool->usage); | |
6c5a688e | 211 | spin_unlock(&pool->counter_pool_lock); |
ff7b0d27 AS |
212 | |
213 | *p_counter_index = entry_index; | |
4e145fc6 JP |
214 | atomic_add(sub_pool->entry_size, &sub_pool->active_entries_count); |
215 | atomic_add(sub_pool->entry_size, &pool->active_entries_count); | |
ff7b0d27 | 216 | return 0; |
6c5a688e IS |
217 | |
218 | err_alloc: | |
219 | spin_unlock(&pool->counter_pool_lock); | |
220 | return err; | |
ff7b0d27 AS |
221 | } |
222 | ||
223 | void mlxsw_sp_counter_free(struct mlxsw_sp *mlxsw_sp, | |
224 | enum mlxsw_sp_counter_sub_pool_id sub_pool_id, | |
225 | unsigned int counter_index) | |
226 | { | |
227 | struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; | |
228 | struct mlxsw_sp_counter_sub_pool *sub_pool; | |
229 | int i; | |
230 | ||
231 | if (WARN_ON(counter_index >= pool->pool_size)) | |
232 | return; | |
c33fbe94 | 233 | sub_pool = &pool->sub_pools[sub_pool_id]; |
6c5a688e | 234 | spin_lock(&pool->counter_pool_lock); |
ff7b0d27 AS |
235 | for (i = 0; i < sub_pool->entry_size; i++) |
236 | __clear_bit(counter_index + i, pool->usage); | |
6c5a688e | 237 | spin_unlock(&pool->counter_pool_lock); |
4e145fc6 JP |
238 | atomic_sub(sub_pool->entry_size, &sub_pool->active_entries_count); |
239 | atomic_sub(sub_pool->entry_size, &pool->active_entries_count); | |
ff7b0d27 | 240 | } |
d53cdbb8 JP |
241 | |
242 | int mlxsw_sp_counter_resources_register(struct mlxsw_core *mlxsw_core) | |
243 | { | |
244 | static struct devlink_resource_size_params size_params; | |
245 | struct devlink *devlink = priv_to_devlink(mlxsw_core); | |
246 | const struct mlxsw_sp_counter_sub_pool *sub_pool; | |
ab8c4cc6 | 247 | unsigned int total_bank_config; |
d53cdbb8 JP |
248 | u64 sub_pool_size; |
249 | u64 base_index; | |
250 | u64 pool_size; | |
251 | u64 bank_size; | |
252 | int err; | |
253 | int i; | |
254 | ||
255 | if (!MLXSW_CORE_RES_VALID(mlxsw_core, COUNTER_POOL_SIZE) || | |
256 | !MLXSW_CORE_RES_VALID(mlxsw_core, COUNTER_BANK_SIZE)) | |
257 | return -EIO; | |
258 | ||
259 | pool_size = MLXSW_CORE_RES_GET(mlxsw_core, COUNTER_POOL_SIZE); | |
260 | bank_size = MLXSW_CORE_RES_GET(mlxsw_core, COUNTER_BANK_SIZE); | |
261 | ||
262 | devlink_resource_size_params_init(&size_params, pool_size, | |
263 | pool_size, bank_size, | |
264 | DEVLINK_RESOURCE_UNIT_ENTRY); | |
72a4c8c9 JP |
265 | err = devl_resource_register(devlink, |
266 | MLXSW_SP_RESOURCE_NAME_COUNTERS, | |
267 | pool_size, | |
268 | MLXSW_SP_RESOURCE_COUNTERS, | |
269 | DEVLINK_RESOURCE_ID_PARENT_TOP, | |
270 | &size_params); | |
d53cdbb8 JP |
271 | if (err) |
272 | return err; | |
273 | ||
274 | /* Allocation is based on bank count which should be | |
275 | * specified for each sub pool statically. | |
276 | */ | |
ab8c4cc6 | 277 | total_bank_config = 0; |
d53cdbb8 JP |
278 | base_index = 0; |
279 | for (i = 0; i < ARRAY_SIZE(mlxsw_sp_counter_sub_pools); i++) { | |
280 | sub_pool = &mlxsw_sp_counter_sub_pools[i]; | |
281 | sub_pool_size = sub_pool->bank_count * bank_size; | |
282 | /* The last bank can't be fully used */ | |
283 | if (base_index + sub_pool_size > pool_size) | |
284 | sub_pool_size = pool_size - base_index; | |
285 | base_index += sub_pool_size; | |
286 | ||
287 | devlink_resource_size_params_init(&size_params, sub_pool_size, | |
288 | sub_pool_size, bank_size, | |
289 | DEVLINK_RESOURCE_UNIT_ENTRY); | |
72a4c8c9 JP |
290 | err = devl_resource_register(devlink, |
291 | sub_pool->resource_name, | |
292 | sub_pool_size, | |
293 | sub_pool->resource_id, | |
294 | MLXSW_SP_RESOURCE_COUNTERS, | |
295 | &size_params); | |
d53cdbb8 JP |
296 | if (err) |
297 | return err; | |
ab8c4cc6 | 298 | total_bank_config += sub_pool->bank_count; |
d53cdbb8 | 299 | } |
ab8c4cc6 JP |
300 | |
301 | /* Check config is valid, no bank over subscription */ | |
c31f0ea7 | 302 | if (WARN_ON(total_bank_config > div64_u64(pool_size, bank_size) + 1)) |
ab8c4cc6 JP |
303 | return -EINVAL; |
304 | ||
d53cdbb8 JP |
305 | return 0; |
306 | } |