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 static u64 mlxsw_sp_kvdl_occ_get(void *priv)
319 {
320 	const struct mlxsw_sp *mlxsw_sp = priv;
321 	u64 occ = 0;
322 	int i;
323 
324 	for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++)
325 		occ += mlxsw_sp_kvdl_part_occ(mlxsw_sp->kvdl->parts[i]);
326 
327 	return occ;
328 }
329 
330 static u64 mlxsw_sp_kvdl_single_occ_get(void *priv)
331 {
332 	const struct mlxsw_sp *mlxsw_sp = priv;
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(void *priv)
340 {
341 	const struct mlxsw_sp *mlxsw_sp = priv;
342 	struct mlxsw_sp_kvdl_part *part;
343 
344 	part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_CHUNKS];
345 	return mlxsw_sp_kvdl_part_occ(part);
346 }
347 
348 static u64 mlxsw_sp_kvdl_large_chunks_occ_get(void *priv)
349 {
350 	const struct mlxsw_sp *mlxsw_sp = priv;
351 	struct mlxsw_sp_kvdl_part *part;
352 
353 	part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_LARGE_CHUNKS];
354 	return mlxsw_sp_kvdl_part_occ(part);
355 }
356 
357 int mlxsw_sp_kvdl_resources_register(struct mlxsw_core *mlxsw_core)
358 {
359 	struct devlink *devlink = priv_to_devlink(mlxsw_core);
360 	static struct devlink_resource_size_params size_params;
361 	u32 kvdl_max_size;
362 	int err;
363 
364 	kvdl_max_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) -
365 			MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) -
366 			MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE);
367 
368 	devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
369 					  MLXSW_SP_KVDL_SINGLE_ALLOC_SIZE,
370 					  DEVLINK_RESOURCE_UNIT_ENTRY);
371 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_SINGLES,
372 					MLXSW_SP_KVDL_SINGLE_SIZE,
373 					MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,
374 					MLXSW_SP_RESOURCE_KVD_LINEAR,
375 					&size_params);
376 	if (err)
377 		return err;
378 
379 	devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
380 					  MLXSW_SP_KVDL_CHUNKS_ALLOC_SIZE,
381 					  DEVLINK_RESOURCE_UNIT_ENTRY);
382 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_CHUNKS,
383 					MLXSW_SP_KVDL_CHUNKS_SIZE,
384 					MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,
385 					MLXSW_SP_RESOURCE_KVD_LINEAR,
386 					&size_params);
387 	if (err)
388 		return err;
389 
390 	devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
391 					  MLXSW_SP_KVDL_LARGE_CHUNKS_ALLOC_SIZE,
392 					  DEVLINK_RESOURCE_UNIT_ENTRY);
393 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_LARGE_CHUNKS,
394 					MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE,
395 					MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,
396 					MLXSW_SP_RESOURCE_KVD_LINEAR,
397 					&size_params);
398 	return err;
399 }
400 
401 int mlxsw_sp_kvdl_init(struct mlxsw_sp *mlxsw_sp)
402 {
403 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
404 	struct mlxsw_sp_kvdl *kvdl;
405 	int err;
406 
407 	kvdl = kzalloc(sizeof(*mlxsw_sp->kvdl), GFP_KERNEL);
408 	if (!kvdl)
409 		return -ENOMEM;
410 	mlxsw_sp->kvdl = kvdl;
411 
412 	err = mlxsw_sp_kvdl_parts_init(mlxsw_sp);
413 	if (err)
414 		goto err_kvdl_parts_init;
415 
416 	devlink_resource_occ_get_register(devlink,
417 					  MLXSW_SP_RESOURCE_KVD_LINEAR,
418 					  mlxsw_sp_kvdl_occ_get,
419 					  mlxsw_sp);
420 	devlink_resource_occ_get_register(devlink,
421 					  MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,
422 					  mlxsw_sp_kvdl_single_occ_get,
423 					  mlxsw_sp);
424 	devlink_resource_occ_get_register(devlink,
425 					  MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,
426 					  mlxsw_sp_kvdl_chunks_occ_get,
427 					  mlxsw_sp);
428 	devlink_resource_occ_get_register(devlink,
429 					  MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,
430 					  mlxsw_sp_kvdl_large_chunks_occ_get,
431 					  mlxsw_sp);
432 
433 	return 0;
434 
435 err_kvdl_parts_init:
436 	kfree(mlxsw_sp->kvdl);
437 	return err;
438 }
439 
440 void mlxsw_sp_kvdl_fini(struct mlxsw_sp *mlxsw_sp)
441 {
442 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
443 
444 	devlink_resource_occ_get_unregister(devlink,
445 					    MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS);
446 	devlink_resource_occ_get_unregister(devlink,
447 					    MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS);
448 	devlink_resource_occ_get_unregister(devlink,
449 					    MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE);
450 	devlink_resource_occ_get_unregister(devlink,
451 					    MLXSW_SP_RESOURCE_KVD_LINEAR);
452 	mlxsw_sp_kvdl_parts_fini(mlxsw_sp);
453 	kfree(mlxsw_sp->kvdl);
454 }
455