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