xref: /openbmc/linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c (revision b240b419db5d624ce7a5a397d6f62a1a686009ec)
1 /*
2  * drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c
3  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the names of the copyright holders nor the names of its
15  *    contributors may be used to endorse or promote products derived from
16  *    this software without specific prior written permission.
17  *
18  * Alternatively, this software may be distributed under the terms of the
19  * GNU General Public License ("GPL") version 2 as published by the Free
20  * Software Foundation.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <linux/kernel.h>
36 #include <linux/bitops.h>
37 
38 #include "spectrum.h"
39 
40 #define MLXSW_SP_KVDL_SINGLE_BASE 0
41 #define MLXSW_SP_KVDL_SINGLE_SIZE 16384
42 #define MLXSW_SP_KVDL_SINGLE_END \
43 	(MLXSW_SP_KVDL_SINGLE_SIZE + MLXSW_SP_KVDL_SINGLE_BASE - 1)
44 
45 #define MLXSW_SP_KVDL_CHUNKS_BASE \
46 	(MLXSW_SP_KVDL_SINGLE_BASE + MLXSW_SP_KVDL_SINGLE_SIZE)
47 #define MLXSW_SP_KVDL_CHUNKS_SIZE 49152
48 #define MLXSW_SP_KVDL_CHUNKS_END \
49 	(MLXSW_SP_KVDL_CHUNKS_SIZE + MLXSW_SP_KVDL_CHUNKS_BASE - 1)
50 
51 #define MLXSW_SP_KVDL_LARGE_CHUNKS_BASE \
52 	(MLXSW_SP_KVDL_CHUNKS_BASE + MLXSW_SP_KVDL_CHUNKS_SIZE)
53 #define MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE \
54 	(MLXSW_SP_KVD_LINEAR_SIZE - MLXSW_SP_KVDL_LARGE_CHUNKS_BASE)
55 #define MLXSW_SP_KVDL_LARGE_CHUNKS_END \
56 	(MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE + MLXSW_SP_KVDL_LARGE_CHUNKS_BASE - 1)
57 
58 #define MLXSW_SP_KVDL_SINGLE_ALLOC_SIZE 1
59 #define MLXSW_SP_KVDL_CHUNKS_ALLOC_SIZE 32
60 #define MLXSW_SP_KVDL_LARGE_CHUNKS_ALLOC_SIZE 512
61 
62 struct mlxsw_sp_kvdl_part_info {
63 	unsigned int part_index;
64 	unsigned int start_index;
65 	unsigned int end_index;
66 	unsigned int alloc_size;
67 	enum mlxsw_sp_resource_id resource_id;
68 };
69 
70 enum mlxsw_sp_kvdl_part_id {
71 	MLXSW_SP_KVDL_PART_ID_SINGLE,
72 	MLXSW_SP_KVDL_PART_ID_CHUNKS,
73 	MLXSW_SP_KVDL_PART_ID_LARGE_CHUNKS,
74 };
75 
76 #define MLXSW_SP_KVDL_PART_INFO(id)				\
77 [MLXSW_SP_KVDL_PART_ID_##id] = {				\
78 	.start_index = MLXSW_SP_KVDL_##id##_BASE,		\
79 	.end_index = MLXSW_SP_KVDL_##id##_END,			\
80 	.alloc_size = MLXSW_SP_KVDL_##id##_ALLOC_SIZE,		\
81 	.resource_id = MLXSW_SP_RESOURCE_KVD_LINEAR_##id,	\
82 }
83 
84 static const struct mlxsw_sp_kvdl_part_info mlxsw_sp_kvdl_parts_info[] = {
85 	MLXSW_SP_KVDL_PART_INFO(SINGLE),
86 	MLXSW_SP_KVDL_PART_INFO(CHUNKS),
87 	MLXSW_SP_KVDL_PART_INFO(LARGE_CHUNKS),
88 };
89 
90 #define MLXSW_SP_KVDL_PARTS_INFO_LEN ARRAY_SIZE(mlxsw_sp_kvdl_parts_info)
91 
92 struct mlxsw_sp_kvdl_part {
93 	struct mlxsw_sp_kvdl_part_info info;
94 	unsigned long usage[0];	/* Entries */
95 };
96 
97 struct mlxsw_sp_kvdl {
98 	struct mlxsw_sp_kvdl_part *parts[MLXSW_SP_KVDL_PARTS_INFO_LEN];
99 };
100 
101 static struct mlxsw_sp_kvdl_part *
102 mlxsw_sp_kvdl_alloc_size_part(struct mlxsw_sp_kvdl *kvdl,
103 			      unsigned int alloc_size)
104 {
105 	struct mlxsw_sp_kvdl_part *part, *min_part = NULL;
106 	int i;
107 
108 	for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) {
109 		part = kvdl->parts[i];
110 		if (alloc_size <= part->info.alloc_size &&
111 		    (!min_part ||
112 		     part->info.alloc_size <= min_part->info.alloc_size))
113 			min_part = part;
114 	}
115 
116 	return min_part ?: ERR_PTR(-ENOBUFS);
117 }
118 
119 static struct mlxsw_sp_kvdl_part *
120 mlxsw_sp_kvdl_index_part(struct mlxsw_sp_kvdl *kvdl, u32 kvdl_index)
121 {
122 	struct mlxsw_sp_kvdl_part *part;
123 	int i;
124 
125 	for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) {
126 		part = kvdl->parts[i];
127 		if (kvdl_index >= part->info.start_index &&
128 		    kvdl_index <= part->info.end_index)
129 			return part;
130 	}
131 
132 	return ERR_PTR(-EINVAL);
133 }
134 
135 static u32
136 mlxsw_sp_entry_index_kvdl_index(const struct mlxsw_sp_kvdl_part_info *info,
137 				unsigned int entry_index)
138 {
139 	return info->start_index + entry_index * info->alloc_size;
140 }
141 
142 static unsigned int
143 mlxsw_sp_kvdl_index_entry_index(const struct mlxsw_sp_kvdl_part_info *info,
144 				u32 kvdl_index)
145 {
146 	return (kvdl_index - info->start_index) / info->alloc_size;
147 }
148 
149 static int mlxsw_sp_kvdl_part_alloc(struct mlxsw_sp_kvdl_part *part,
150 				    u32 *p_kvdl_index)
151 {
152 	const struct mlxsw_sp_kvdl_part_info *info = &part->info;
153 	unsigned int entry_index, nr_entries;
154 
155 	nr_entries = (info->end_index - info->start_index + 1) /
156 		     info->alloc_size;
157 	entry_index = find_first_zero_bit(part->usage, nr_entries);
158 	if (entry_index == nr_entries)
159 		return -ENOBUFS;
160 	__set_bit(entry_index, part->usage);
161 
162 	*p_kvdl_index = mlxsw_sp_entry_index_kvdl_index(info, entry_index);
163 
164 	return 0;
165 }
166 
167 static void mlxsw_sp_kvdl_part_free(struct mlxsw_sp_kvdl_part *part,
168 				    u32 kvdl_index)
169 {
170 	const struct mlxsw_sp_kvdl_part_info *info = &part->info;
171 	unsigned int entry_index;
172 
173 	entry_index = mlxsw_sp_kvdl_index_entry_index(info, kvdl_index);
174 	__clear_bit(entry_index, part->usage);
175 }
176 
177 int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count,
178 			u32 *p_entry_index)
179 {
180 	struct mlxsw_sp_kvdl_part *part;
181 
182 	/* Find partition with smallest allocation size satisfying the
183 	 * requested size.
184 	 */
185 	part = mlxsw_sp_kvdl_alloc_size_part(mlxsw_sp->kvdl, entry_count);
186 	if (IS_ERR(part))
187 		return PTR_ERR(part);
188 
189 	return mlxsw_sp_kvdl_part_alloc(part, p_entry_index);
190 }
191 
192 void mlxsw_sp_kvdl_free(struct mlxsw_sp *mlxsw_sp, int entry_index)
193 {
194 	struct mlxsw_sp_kvdl_part *part;
195 
196 	part = mlxsw_sp_kvdl_index_part(mlxsw_sp->kvdl, entry_index);
197 	if (IS_ERR(part))
198 		return;
199 	mlxsw_sp_kvdl_part_free(part, entry_index);
200 }
201 
202 int mlxsw_sp_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp,
203 				   unsigned int entry_count,
204 				   unsigned int *p_alloc_size)
205 {
206 	struct mlxsw_sp_kvdl_part *part;
207 
208 	part = mlxsw_sp_kvdl_alloc_size_part(mlxsw_sp->kvdl, entry_count);
209 	if (IS_ERR(part))
210 		return PTR_ERR(part);
211 
212 	*p_alloc_size = part->info.alloc_size;
213 
214 	return 0;
215 }
216 
217 static void mlxsw_sp_kvdl_part_update(struct mlxsw_sp_kvdl_part *part,
218 				      struct mlxsw_sp_kvdl_part *part_prev,
219 				      unsigned int size)
220 {
221 
222 	if (!part_prev) {
223 		part->info.end_index = size - 1;
224 	} else {
225 		part->info.start_index = part_prev->info.end_index + 1;
226 		part->info.end_index = part->info.start_index + size - 1;
227 	}
228 }
229 
230 static struct mlxsw_sp_kvdl_part *
231 mlxsw_sp_kvdl_part_init(struct mlxsw_sp *mlxsw_sp,
232 			const struct mlxsw_sp_kvdl_part_info *info,
233 			struct mlxsw_sp_kvdl_part *part_prev)
234 {
235 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
236 	struct mlxsw_sp_kvdl_part *part;
237 	bool need_update = true;
238 	unsigned int nr_entries;
239 	size_t usage_size;
240 	u64 resource_size;
241 	int err;
242 
243 	err = devlink_resource_size_get(devlink, info->resource_id,
244 					&resource_size);
245 	if (err) {
246 		need_update = false;
247 		resource_size = info->end_index - info->start_index + 1;
248 	}
249 
250 	nr_entries = div_u64(resource_size, info->alloc_size);
251 	usage_size = BITS_TO_LONGS(nr_entries) * sizeof(unsigned long);
252 	part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL);
253 	if (!part)
254 		return ERR_PTR(-ENOMEM);
255 
256 	memcpy(&part->info, info, sizeof(part->info));
257 
258 	if (need_update)
259 		mlxsw_sp_kvdl_part_update(part, part_prev, resource_size);
260 	return part;
261 }
262 
263 static void mlxsw_sp_kvdl_part_fini(struct mlxsw_sp_kvdl_part *part)
264 {
265 	kfree(part);
266 }
267 
268 static int mlxsw_sp_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp)
269 {
270 	struct mlxsw_sp_kvdl *kvdl = mlxsw_sp->kvdl;
271 	const struct mlxsw_sp_kvdl_part_info *info;
272 	struct mlxsw_sp_kvdl_part *part_prev = NULL;
273 	int err, i;
274 
275 	for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) {
276 		info = &mlxsw_sp_kvdl_parts_info[i];
277 		kvdl->parts[i] = mlxsw_sp_kvdl_part_init(mlxsw_sp, info,
278 							 part_prev);
279 		if (IS_ERR(kvdl->parts[i])) {
280 			err = PTR_ERR(kvdl->parts[i]);
281 			goto err_kvdl_part_init;
282 		}
283 		part_prev = kvdl->parts[i];
284 	}
285 	return 0;
286 
287 err_kvdl_part_init:
288 	for (i--; i >= 0; i--)
289 		mlxsw_sp_kvdl_part_fini(kvdl->parts[i]);
290 	return err;
291 }
292 
293 static void mlxsw_sp_kvdl_parts_fini(struct mlxsw_sp *mlxsw_sp)
294 {
295 	struct mlxsw_sp_kvdl *kvdl = mlxsw_sp->kvdl;
296 	int i;
297 
298 	for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++)
299 		mlxsw_sp_kvdl_part_fini(kvdl->parts[i]);
300 }
301 
302 static u64 mlxsw_sp_kvdl_part_occ(struct mlxsw_sp_kvdl_part *part)
303 {
304 	const struct mlxsw_sp_kvdl_part_info *info = &part->info;
305 	unsigned int nr_entries;
306 	int bit = -1;
307 	u64 occ = 0;
308 
309 	nr_entries = (info->end_index -
310 		      info->start_index + 1) /
311 		      info->alloc_size;
312 	while ((bit = find_next_bit(part->usage, nr_entries, bit + 1))
313 		< nr_entries)
314 		occ += info->alloc_size;
315 	return occ;
316 }
317 
318 u64 mlxsw_sp_kvdl_occ_get(const struct mlxsw_sp *mlxsw_sp)
319 {
320 	u64 occ = 0;
321 	int i;
322 
323 	for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++)
324 		occ += mlxsw_sp_kvdl_part_occ(mlxsw_sp->kvdl->parts[i]);
325 
326 	return occ;
327 }
328 
329 static u64 mlxsw_sp_kvdl_single_occ_get(struct devlink *devlink)
330 {
331 	struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
332 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
333 	struct mlxsw_sp_kvdl_part *part;
334 
335 	part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_SINGLE];
336 	return mlxsw_sp_kvdl_part_occ(part);
337 }
338 
339 static u64 mlxsw_sp_kvdl_chunks_occ_get(struct devlink *devlink)
340 {
341 	struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
342 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
343 	struct mlxsw_sp_kvdl_part *part;
344 
345 	part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_CHUNKS];
346 	return mlxsw_sp_kvdl_part_occ(part);
347 }
348 
349 static u64 mlxsw_sp_kvdl_large_chunks_occ_get(struct devlink *devlink)
350 {
351 	struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
352 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
353 	struct mlxsw_sp_kvdl_part *part;
354 
355 	part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_LARGE_CHUNKS];
356 	return mlxsw_sp_kvdl_part_occ(part);
357 }
358 
359 static const struct devlink_resource_ops mlxsw_sp_kvdl_single_ops = {
360 	.occ_get = mlxsw_sp_kvdl_single_occ_get,
361 };
362 
363 static const struct devlink_resource_ops mlxsw_sp_kvdl_chunks_ops = {
364 	.occ_get = mlxsw_sp_kvdl_chunks_occ_get,
365 };
366 
367 static const struct devlink_resource_ops mlxsw_sp_kvdl_chunks_large_ops = {
368 	.occ_get = mlxsw_sp_kvdl_large_chunks_occ_get,
369 };
370 
371 int mlxsw_sp_kvdl_resources_register(struct mlxsw_core *mlxsw_core)
372 {
373 	struct devlink *devlink = priv_to_devlink(mlxsw_core);
374 	static struct devlink_resource_size_params size_params;
375 	u32 kvdl_max_size;
376 	int err;
377 
378 	kvdl_max_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) -
379 			MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) -
380 			MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE);
381 
382 	devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
383 					  MLXSW_SP_KVDL_SINGLE_ALLOC_SIZE,
384 					  DEVLINK_RESOURCE_UNIT_ENTRY);
385 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_SINGLES,
386 					MLXSW_SP_KVDL_SINGLE_SIZE,
387 					MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,
388 					MLXSW_SP_RESOURCE_KVD_LINEAR,
389 					&size_params,
390 					&mlxsw_sp_kvdl_single_ops);
391 	if (err)
392 		return err;
393 
394 	devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
395 					  MLXSW_SP_KVDL_CHUNKS_ALLOC_SIZE,
396 					  DEVLINK_RESOURCE_UNIT_ENTRY);
397 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_CHUNKS,
398 					MLXSW_SP_KVDL_CHUNKS_SIZE,
399 					MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,
400 					MLXSW_SP_RESOURCE_KVD_LINEAR,
401 					&size_params,
402 					&mlxsw_sp_kvdl_chunks_ops);
403 	if (err)
404 		return err;
405 
406 	devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
407 					  MLXSW_SP_KVDL_LARGE_CHUNKS_ALLOC_SIZE,
408 					  DEVLINK_RESOURCE_UNIT_ENTRY);
409 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_LARGE_CHUNKS,
410 					MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE,
411 					MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,
412 					MLXSW_SP_RESOURCE_KVD_LINEAR,
413 					&size_params,
414 					&mlxsw_sp_kvdl_chunks_large_ops);
415 	return err;
416 }
417 
418 int mlxsw_sp_kvdl_init(struct mlxsw_sp *mlxsw_sp)
419 {
420 	struct mlxsw_sp_kvdl *kvdl;
421 	int err;
422 
423 	kvdl = kzalloc(sizeof(*mlxsw_sp->kvdl), GFP_KERNEL);
424 	if (!kvdl)
425 		return -ENOMEM;
426 	mlxsw_sp->kvdl = kvdl;
427 
428 	err = mlxsw_sp_kvdl_parts_init(mlxsw_sp);
429 	if (err)
430 		goto err_kvdl_parts_init;
431 
432 	return 0;
433 
434 err_kvdl_parts_init:
435 	kfree(mlxsw_sp->kvdl);
436 	return err;
437 }
438 
439 void mlxsw_sp_kvdl_fini(struct mlxsw_sp *mlxsw_sp)
440 {
441 	mlxsw_sp_kvdl_parts_fini(mlxsw_sp);
442 	kfree(mlxsw_sp->kvdl);
443 }
444