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 } 376