net/bnxt: decrease log level of a debug message
[dpdk.git] / drivers / net / bnxt / tf_core / dpool.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2019-2021 Broadcom
3  * All rights reserved.
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include <errno.h>
10
11 #include <rte_malloc.h>
12
13 #include "tfp.h"
14 #include "dpool.h"
15
16 int dpool_init(struct dpool *dpool,
17                uint32_t start_index,
18                uint32_t size,
19                uint8_t max_alloc_size,
20                void *user_data,
21                int (*move_callback)(void *, uint64_t, uint32_t))
22 {
23         uint32_t i;
24         int rc;
25         struct tfp_calloc_parms parms;
26
27         parms.nitems = size;
28         parms.size = sizeof(struct dpool_entry);
29         parms.alignment = 0;
30
31         rc = tfp_calloc(&parms);
32
33         if (rc)
34                 return rc;
35
36         dpool->entry = parms.mem_va;
37         dpool->start_index = start_index;
38         dpool->size = size;
39         dpool->max_alloc_size = max_alloc_size;
40         dpool->user_data = user_data;
41         dpool->move_callback = move_callback;
42         /*
43          * Init entries
44          */
45         for (i = 0; i < size; i++) {
46                 dpool->entry[i].flags = 0;
47                 dpool->entry[i].index = start_index;
48                 dpool->entry[i].entry_data = 0UL;
49                 start_index++;
50         }
51
52         return 0;
53 }
54
55 static int dpool_move(struct dpool *dpool,
56                       uint32_t dst_index,
57                       uint32_t src_index)
58 {
59         uint32_t size;
60         uint32_t i;
61         if (DP_IS_FREE(dpool->entry[dst_index].flags)) {
62                 size = DP_FLAGS_SIZE(dpool->entry[src_index].flags);
63
64                 dpool->entry[dst_index].flags = dpool->entry[src_index].flags;
65                 dpool->entry[dst_index].entry_data = dpool->entry[src_index].entry_data;
66
67                 if (dpool->move_callback != NULL) {
68                         dpool->move_callback(dpool->user_data,
69                                              dpool->entry[src_index].entry_data,
70                                              dst_index + dpool->start_index);
71                 }
72
73                 dpool->entry[src_index].flags = 0;
74                 dpool->entry[src_index].entry_data = 0UL;
75
76                 for (i = 1; i < size; i++) {
77                         dpool->entry[dst_index + i].flags = size;
78                         dpool->entry[src_index + i].flags = 0;
79                 }
80         } else {
81                 return -1;
82         }
83
84         return 0;
85 }
86
87
88 int dpool_defrag(struct dpool *dpool,
89                  uint32_t entry_size,
90                  uint8_t defrag)
91 {
92         struct dpool_free_list *free_list;
93         struct dpool_adj_list *adj_list;
94         uint32_t count;
95         uint32_t index;
96         uint32_t used;
97         uint32_t i;
98         uint32_t size;
99         uint32_t largest_free_index = 0;
100         uint32_t largest_free_size;
101         uint32_t max;
102         uint32_t max_index;
103         uint32_t max_size = 0;
104         int rc;
105
106         free_list = rte_zmalloc("dpool_free_list",
107                                 sizeof(struct dpool_free_list), 0);
108         if (free_list == NULL) {
109                 TFP_DRV_LOG(ERR, "dpool free list allocation failed\n");
110                 return -ENOMEM;
111         }
112
113         adj_list = rte_zmalloc("dpool_adjacent_list",
114                                 sizeof(struct dpool_adj_list), 0);
115         if (adj_list == NULL) {
116                 TFP_DRV_LOG(ERR, "dpool adjacent list allocation failed\n");
117                 return -ENOMEM;
118         }
119
120         while (1) {
121                 /*
122                  * Create list of free entries
123                  */
124                 free_list->size = 0;
125                 largest_free_size = 0;
126                 largest_free_index = 0;
127                 count = 0;
128                 index = 0;
129
130                 for (i = 0; i < dpool->size; i++) {
131                         if (DP_IS_FREE(dpool->entry[i].flags)) {
132                                 if (count == 0)
133                                         index = i;
134                                 count++;
135                         } else if (count > 0) {
136                                 free_list->entry[free_list->size].index = index;
137                                 free_list->entry[free_list->size].size = count;
138
139                                 if (count > largest_free_size) {
140                                         largest_free_index = free_list->size;
141                                         largest_free_size = count;
142                                 }
143
144                                 free_list->size++;
145                                 count = 0;
146                         }
147                 }
148
149                 if (free_list->size == 0)
150                         largest_free_size = count;
151
152                 /*
153                  * If using defrag to fit and there's a large enough
154                  * space then we are done.
155                  */
156                 if (defrag == DP_DEFRAG_TO_FIT &&
157                     largest_free_size >= entry_size)
158                         goto done;
159
160                 /*
161                  * Create list of entries adjacent to free entries
162                  */
163                 count = 0;
164                 adj_list->size = 0;
165                 used = 0;
166
167                 for (i = 0; i < dpool->size; ) {
168                         if (DP_IS_USED(dpool->entry[i].flags)) {
169                                 used++;
170
171                                 if (count > 0) {
172                                         adj_list->entry[adj_list->size].index = i;
173                                         adj_list->entry[adj_list->size].size =
174                                                 DP_FLAGS_SIZE(dpool->entry[i].flags);
175                                         adj_list->entry[adj_list->size].left = count;
176
177                                         if (adj_list->size > 0 && used == 1)
178                                                 adj_list->entry[adj_list->size - 1].right = count;
179
180                                         adj_list->size++;
181                                 }
182
183                                 count = 0;
184                                 i += DP_FLAGS_SIZE(dpool->entry[i].flags);
185                         } else {
186                                 used = 0;
187                                 count++;
188                                 i++;
189                         }
190                 }
191
192                 /*
193                  * Using the size of the largest free space available
194                  * select the adjacency list entry of that size with
195                  * the largest left + right + size count. If there
196                  * are no entries of that size then decrement the size
197                  * and try again.
198                  */
199                 max = 0;
200                 max_index = 0;
201                 max_size = 0;
202
203                 for (size = largest_free_size; size > 0; size--) {
204                         for (i = 0; i < adj_list->size; i++) {
205                                 if (adj_list->entry[i].size == size &&
206                                     ((size +
207                                       adj_list->entry[i].left +
208                                       adj_list->entry[i].right) > max)) {
209                                         max = size +
210                                                 adj_list->entry[i].left +
211                                                 adj_list->entry[i].right;
212                                         max_size = size;
213                                         max_index = adj_list->entry[i].index;
214                                 }
215                         }
216
217                         if (max)
218                                 break;
219                 }
220
221                 /*
222                  * If the max entry is smaller than the largest_free_size
223                  * find the first entry in the free list that it cn fit in to.
224                  */
225                 if (max_size < largest_free_size) {
226                         for (i = 0; i < free_list->size; i++) {
227                                 if (free_list->entry[i].size >= max_size) {
228                                         largest_free_index = i;
229                                         break;
230                                 }
231                         }
232                 }
233
234                 /*
235                  * If we have a contender then move it to the new spot.
236                  */
237                 if (max) {
238                         rc = dpool_move(dpool,
239                                         free_list->entry[largest_free_index].index,
240                                         max_index);
241                         if (rc) {
242                                 rte_free(free_list);
243                                 rte_free(adj_list);
244                                 return rc;
245                         }
246                 } else {
247                         break;
248                 }
249         }
250
251 done:
252         rte_free(free_list);
253         rte_free(adj_list);
254         return largest_free_size;
255 }
256
257
258 uint32_t dpool_alloc(struct dpool *dpool,
259                      uint32_t size,
260                      uint8_t defrag)
261 {
262         uint32_t i;
263         uint32_t j;
264         uint32_t count = 0;
265         uint32_t first_entry_index;
266         int rc;
267
268         if (size > dpool->max_alloc_size || size == 0)
269                 return DP_INVALID_INDEX;
270
271         /*
272          * Defrag requires EM move support.
273          */
274         if (defrag != DP_DEFRAG_NONE &&
275             dpool->move_callback == NULL)
276                 return DP_INVALID_INDEX;
277
278         while (1) {
279                 /*
280                  * find <size> consecutive free entries
281                  */
282                 for (i = 0; i < dpool->size; i++) {
283                         if (DP_IS_FREE(dpool->entry[i].flags)) {
284                                 if (count == 0)
285                                         first_entry_index = i;
286
287                                 count++;
288
289                                 if (count == size) {
290                                         for (j = 0; j < size; j++) {
291                                                 dpool->entry[j + first_entry_index].flags = size;
292                                                 if (j == 0)
293                                                         dpool->entry[j + first_entry_index].flags |=
294                                                                 DP_FLAGS_START;
295                                         }
296
297                                         dpool->entry[i].entry_data = 0UL;
298                                         return (first_entry_index + dpool->start_index);
299                                 }
300                         } else {
301                                 count = 0;
302                         }
303                 }
304
305                 /*
306                  * If defragging then do it to it
307                  */
308                 if (defrag != DP_DEFRAG_NONE) {
309                         rc = dpool_defrag(dpool, size, defrag);
310
311                         if (rc < 0)
312                                 return DP_INVALID_INDEX;
313                 } else {
314                         break;
315                 }
316
317                 /*
318                  * If the defrag created enough space then try the
319                  * alloc again else quit.
320                  */
321                 if ((uint32_t)rc < size)
322                         break;
323         }
324
325         return DP_INVALID_INDEX;
326 }
327
328 int dpool_free(struct dpool *dpool,
329                uint32_t index)
330 {
331         uint32_t i;
332         int start = (index - dpool->start_index);
333         uint32_t size;
334
335         if (start < 0)
336                 return -1;
337
338         if (DP_IS_START(dpool->entry[start].flags)) {
339                 size = DP_FLAGS_SIZE(dpool->entry[start].flags);
340                 if (size > dpool->max_alloc_size || size == 0)
341                         return -1;
342
343                 for (i = start; i < (start + size); i++)
344                         dpool->entry[i].flags = 0;
345
346                 return 0;
347         }
348
349         return -1;
350 }
351
352 void dpool_free_all(struct dpool *dpool)
353 {
354         uint32_t i;
355
356         for (i = 0; i < dpool->size; i++)
357                 dpool_free(dpool, dpool->entry[i].index);
358 }
359
360 int dpool_set_entry_data(struct dpool *dpool,
361                          uint32_t index,
362                          uint64_t entry_data)
363 {
364         int start = (index - dpool->start_index);
365
366         if (start < 0)
367                 return -1;
368
369         if (DP_IS_START(dpool->entry[start].flags)) {
370                 dpool->entry[start].entry_data = entry_data;
371                 return 0;
372         }
373
374         return -1;
375 }