105b405d5SPeter Spreadborough /* SPDX-License-Identifier: BSD-3-Clause
2*e6e8f03eSRandy Schacher * Copyright(c) 2019-2023 Broadcom
305b405d5SPeter Spreadborough * All rights reserved.
405b405d5SPeter Spreadborough */
505b405d5SPeter Spreadborough #include <stdio.h>
605b405d5SPeter Spreadborough #include <stdlib.h>
705b405d5SPeter Spreadborough #include <stdbool.h>
805b405d5SPeter Spreadborough #include <stdint.h>
905b405d5SPeter Spreadborough #include <errno.h>
1005b405d5SPeter Spreadborough #include "tfp.h"
1105b405d5SPeter Spreadborough #include "dpool.h"
1205b405d5SPeter Spreadborough
dpool_init(struct dpool * dpool,uint32_t start_index,uint32_t size,uint8_t max_alloc_size,void * user_data,int (* move_callback)(void *,uint64_t,uint32_t))1305b405d5SPeter Spreadborough int dpool_init(struct dpool *dpool,
1405b405d5SPeter Spreadborough uint32_t start_index,
1505b405d5SPeter Spreadborough uint32_t size,
1605b405d5SPeter Spreadborough uint8_t max_alloc_size,
1705b405d5SPeter Spreadborough void *user_data,
1805b405d5SPeter Spreadborough int (*move_callback)(void *, uint64_t, uint32_t))
1905b405d5SPeter Spreadborough {
2005b405d5SPeter Spreadborough uint32_t i;
2105b405d5SPeter Spreadborough int rc;
2205b405d5SPeter Spreadborough struct tfp_calloc_parms parms;
2305b405d5SPeter Spreadborough
2405b405d5SPeter Spreadborough parms.nitems = size;
2505b405d5SPeter Spreadborough parms.size = sizeof(struct dpool_entry);
2605b405d5SPeter Spreadborough parms.alignment = 0;
2705b405d5SPeter Spreadborough
2805b405d5SPeter Spreadborough rc = tfp_calloc(&parms);
2905b405d5SPeter Spreadborough
3005b405d5SPeter Spreadborough if (rc)
3105b405d5SPeter Spreadborough return rc;
3205b405d5SPeter Spreadborough
3305b405d5SPeter Spreadborough dpool->entry = parms.mem_va;
3405b405d5SPeter Spreadborough dpool->start_index = start_index;
3505b405d5SPeter Spreadborough dpool->size = size;
3605b405d5SPeter Spreadborough dpool->max_alloc_size = max_alloc_size;
3705b405d5SPeter Spreadborough dpool->user_data = user_data;
3805b405d5SPeter Spreadborough dpool->move_callback = move_callback;
3905b405d5SPeter Spreadborough /*
4005b405d5SPeter Spreadborough * Init entries
4105b405d5SPeter Spreadborough */
4205b405d5SPeter Spreadborough for (i = 0; i < size; i++) {
4305b405d5SPeter Spreadborough dpool->entry[i].flags = 0;
4405b405d5SPeter Spreadborough dpool->entry[i].index = start_index;
4505b405d5SPeter Spreadborough dpool->entry[i].entry_data = 0UL;
4605b405d5SPeter Spreadborough start_index++;
4705b405d5SPeter Spreadborough }
4805b405d5SPeter Spreadborough
4905b405d5SPeter Spreadborough return 0;
5005b405d5SPeter Spreadborough }
5105b405d5SPeter Spreadborough
dpool_move(struct dpool * dpool,uint32_t dst_index,uint32_t src_index)5205b405d5SPeter Spreadborough static int dpool_move(struct dpool *dpool,
5305b405d5SPeter Spreadborough uint32_t dst_index,
5405b405d5SPeter Spreadborough uint32_t src_index)
5505b405d5SPeter Spreadborough {
5605b405d5SPeter Spreadborough uint32_t size;
5705b405d5SPeter Spreadborough uint32_t i;
58*e6e8f03eSRandy Schacher
5905b405d5SPeter Spreadborough if (DP_IS_FREE(dpool->entry[dst_index].flags)) {
6005b405d5SPeter Spreadborough size = DP_FLAGS_SIZE(dpool->entry[src_index].flags);
6105b405d5SPeter Spreadborough
6205b405d5SPeter Spreadborough dpool->entry[dst_index].flags = dpool->entry[src_index].flags;
6305b405d5SPeter Spreadborough dpool->entry[dst_index].entry_data = dpool->entry[src_index].entry_data;
6405b405d5SPeter Spreadborough
6505b405d5SPeter Spreadborough if (dpool->move_callback != NULL) {
6605b405d5SPeter Spreadborough dpool->move_callback(dpool->user_data,
6705b405d5SPeter Spreadborough dpool->entry[src_index].entry_data,
6805b405d5SPeter Spreadborough dst_index + dpool->start_index);
6905b405d5SPeter Spreadborough }
7005b405d5SPeter Spreadborough
7105b405d5SPeter Spreadborough dpool->entry[src_index].flags = 0;
7205b405d5SPeter Spreadborough dpool->entry[src_index].entry_data = 0UL;
7305b405d5SPeter Spreadborough
7405b405d5SPeter Spreadborough for (i = 1; i < size; i++) {
7505b405d5SPeter Spreadborough dpool->entry[dst_index + i].flags = size;
7605b405d5SPeter Spreadborough dpool->entry[src_index + i].flags = 0;
7705b405d5SPeter Spreadborough }
7805b405d5SPeter Spreadborough } else {
7905b405d5SPeter Spreadborough return -1;
8005b405d5SPeter Spreadborough }
8105b405d5SPeter Spreadborough
8205b405d5SPeter Spreadborough return 0;
8305b405d5SPeter Spreadborough }
8405b405d5SPeter Spreadborough
dpool_defrag(struct dpool * dpool,uint32_t entry_size,uint8_t defrag)8505b405d5SPeter Spreadborough int dpool_defrag(struct dpool *dpool,
8605b405d5SPeter Spreadborough uint32_t entry_size,
8705b405d5SPeter Spreadborough uint8_t defrag)
8805b405d5SPeter Spreadborough {
8905b405d5SPeter Spreadborough struct dpool_free_list *free_list;
9005b405d5SPeter Spreadborough struct dpool_adj_list *adj_list;
91adf0802eSRandy Schacher struct tfp_calloc_parms parms;
9205b405d5SPeter Spreadborough uint32_t count;
9305b405d5SPeter Spreadborough uint32_t index;
9405b405d5SPeter Spreadborough uint32_t used;
9505b405d5SPeter Spreadborough uint32_t i;
9605b405d5SPeter Spreadborough uint32_t size;
9705b405d5SPeter Spreadborough uint32_t largest_free_index = 0;
9805b405d5SPeter Spreadborough uint32_t largest_free_size;
9905b405d5SPeter Spreadborough uint32_t max;
10005b405d5SPeter Spreadborough uint32_t max_index;
10105b405d5SPeter Spreadborough uint32_t max_size = 0;
10205b405d5SPeter Spreadborough int rc;
10305b405d5SPeter Spreadborough
104adf0802eSRandy Schacher parms.nitems = 1;
105adf0802eSRandy Schacher parms.size = sizeof(struct dpool_free_list);
106adf0802eSRandy Schacher parms.alignment = 0;
107adf0802eSRandy Schacher
108adf0802eSRandy Schacher rc = tfp_calloc(&parms);
109adf0802eSRandy Schacher
110adf0802eSRandy Schacher if (rc)
111adf0802eSRandy Schacher return rc;
112adf0802eSRandy Schacher
113adf0802eSRandy Schacher free_list = (struct dpool_free_list *)parms.mem_va;
11405b405d5SPeter Spreadborough if (free_list == NULL) {
11505b405d5SPeter Spreadborough TFP_DRV_LOG(ERR, "dpool free list allocation failed\n");
11605b405d5SPeter Spreadborough return -ENOMEM;
11705b405d5SPeter Spreadborough }
11805b405d5SPeter Spreadborough
119adf0802eSRandy Schacher parms.nitems = 1;
120adf0802eSRandy Schacher parms.size = sizeof(struct dpool_adj_list);
121adf0802eSRandy Schacher parms.alignment = 0;
122adf0802eSRandy Schacher
123adf0802eSRandy Schacher rc = tfp_calloc(&parms);
124adf0802eSRandy Schacher
125adf0802eSRandy Schacher if (rc)
126adf0802eSRandy Schacher return rc;
127adf0802eSRandy Schacher
128adf0802eSRandy Schacher adj_list = (struct dpool_adj_list *)parms.mem_va;
12905b405d5SPeter Spreadborough if (adj_list == NULL) {
13005b405d5SPeter Spreadborough TFP_DRV_LOG(ERR, "dpool adjacent list allocation failed\n");
13105b405d5SPeter Spreadborough return -ENOMEM;
13205b405d5SPeter Spreadborough }
13305b405d5SPeter Spreadborough
13405b405d5SPeter Spreadborough while (1) {
13505b405d5SPeter Spreadborough /*
13605b405d5SPeter Spreadborough * Create list of free entries
13705b405d5SPeter Spreadborough */
13805b405d5SPeter Spreadborough free_list->size = 0;
13905b405d5SPeter Spreadborough largest_free_size = 0;
14005b405d5SPeter Spreadborough largest_free_index = 0;
14105b405d5SPeter Spreadborough count = 0;
14272d7b595SAjit Khaparde index = 0;
14305b405d5SPeter Spreadborough
14405b405d5SPeter Spreadborough for (i = 0; i < dpool->size; i++) {
14505b405d5SPeter Spreadborough if (DP_IS_FREE(dpool->entry[i].flags)) {
14605b405d5SPeter Spreadborough if (count == 0)
14705b405d5SPeter Spreadborough index = i;
14805b405d5SPeter Spreadborough count++;
14905b405d5SPeter Spreadborough } else if (count > 0) {
15005b405d5SPeter Spreadborough free_list->entry[free_list->size].index = index;
15105b405d5SPeter Spreadborough free_list->entry[free_list->size].size = count;
15205b405d5SPeter Spreadborough
15305b405d5SPeter Spreadborough if (count > largest_free_size) {
15405b405d5SPeter Spreadborough largest_free_index = free_list->size;
15505b405d5SPeter Spreadborough largest_free_size = count;
15605b405d5SPeter Spreadborough }
15705b405d5SPeter Spreadborough
15805b405d5SPeter Spreadborough free_list->size++;
15905b405d5SPeter Spreadborough count = 0;
16005b405d5SPeter Spreadborough }
16105b405d5SPeter Spreadborough }
16205b405d5SPeter Spreadborough
16305b405d5SPeter Spreadborough if (free_list->size == 0)
16405b405d5SPeter Spreadborough largest_free_size = count;
16505b405d5SPeter Spreadborough
16605b405d5SPeter Spreadborough /*
16705b405d5SPeter Spreadborough * If using defrag to fit and there's a large enough
16805b405d5SPeter Spreadborough * space then we are done.
16905b405d5SPeter Spreadborough */
17005b405d5SPeter Spreadborough if (defrag == DP_DEFRAG_TO_FIT &&
17105b405d5SPeter Spreadborough largest_free_size >= entry_size)
17205b405d5SPeter Spreadborough goto done;
17305b405d5SPeter Spreadborough
17405b405d5SPeter Spreadborough /*
17505b405d5SPeter Spreadborough * Create list of entries adjacent to free entries
17605b405d5SPeter Spreadborough */
17705b405d5SPeter Spreadborough count = 0;
17805b405d5SPeter Spreadborough adj_list->size = 0;
17905b405d5SPeter Spreadborough used = 0;
18005b405d5SPeter Spreadborough
18105b405d5SPeter Spreadborough for (i = 0; i < dpool->size; ) {
18205b405d5SPeter Spreadborough if (DP_IS_USED(dpool->entry[i].flags)) {
18305b405d5SPeter Spreadborough used++;
18405b405d5SPeter Spreadborough
18505b405d5SPeter Spreadborough if (count > 0) {
18605b405d5SPeter Spreadborough adj_list->entry[adj_list->size].index = i;
18705b405d5SPeter Spreadborough adj_list->entry[adj_list->size].size =
18805b405d5SPeter Spreadborough DP_FLAGS_SIZE(dpool->entry[i].flags);
18905b405d5SPeter Spreadborough adj_list->entry[adj_list->size].left = count;
19005b405d5SPeter Spreadborough
19105b405d5SPeter Spreadborough if (adj_list->size > 0 && used == 1)
19205b405d5SPeter Spreadborough adj_list->entry[adj_list->size - 1].right = count;
19305b405d5SPeter Spreadborough
19405b405d5SPeter Spreadborough adj_list->size++;
19505b405d5SPeter Spreadborough }
19605b405d5SPeter Spreadborough
19705b405d5SPeter Spreadborough count = 0;
19805b405d5SPeter Spreadborough i += DP_FLAGS_SIZE(dpool->entry[i].flags);
19905b405d5SPeter Spreadborough } else {
20005b405d5SPeter Spreadborough used = 0;
20105b405d5SPeter Spreadborough count++;
20205b405d5SPeter Spreadborough i++;
20305b405d5SPeter Spreadborough }
20405b405d5SPeter Spreadborough }
20505b405d5SPeter Spreadborough
20605b405d5SPeter Spreadborough /*
20705b405d5SPeter Spreadborough * Using the size of the largest free space available
20805b405d5SPeter Spreadborough * select the adjacency list entry of that size with
20905b405d5SPeter Spreadborough * the largest left + right + size count. If there
21005b405d5SPeter Spreadborough * are no entries of that size then decrement the size
21105b405d5SPeter Spreadborough * and try again.
21205b405d5SPeter Spreadborough */
21305b405d5SPeter Spreadborough max = 0;
21405b405d5SPeter Spreadborough max_index = 0;
21505b405d5SPeter Spreadborough max_size = 0;
21605b405d5SPeter Spreadborough
21705b405d5SPeter Spreadborough for (size = largest_free_size; size > 0; size--) {
21805b405d5SPeter Spreadborough for (i = 0; i < adj_list->size; i++) {
21905b405d5SPeter Spreadborough if (adj_list->entry[i].size == size &&
22005b405d5SPeter Spreadborough ((size +
22105b405d5SPeter Spreadborough adj_list->entry[i].left +
22205b405d5SPeter Spreadborough adj_list->entry[i].right) > max)) {
22305b405d5SPeter Spreadborough max = size +
22405b405d5SPeter Spreadborough adj_list->entry[i].left +
22505b405d5SPeter Spreadborough adj_list->entry[i].right;
22605b405d5SPeter Spreadborough max_size = size;
22705b405d5SPeter Spreadborough max_index = adj_list->entry[i].index;
22805b405d5SPeter Spreadborough }
22905b405d5SPeter Spreadborough }
23005b405d5SPeter Spreadborough
23105b405d5SPeter Spreadborough if (max)
23205b405d5SPeter Spreadborough break;
23305b405d5SPeter Spreadborough }
23405b405d5SPeter Spreadborough
23505b405d5SPeter Spreadborough /*
23605b405d5SPeter Spreadborough * If the max entry is smaller than the largest_free_size
23705b405d5SPeter Spreadborough * find the first entry in the free list that it cn fit in to.
23805b405d5SPeter Spreadborough */
23905b405d5SPeter Spreadborough if (max_size < largest_free_size) {
24005b405d5SPeter Spreadborough for (i = 0; i < free_list->size; i++) {
24105b405d5SPeter Spreadborough if (free_list->entry[i].size >= max_size) {
24205b405d5SPeter Spreadborough largest_free_index = i;
24305b405d5SPeter Spreadborough break;
24405b405d5SPeter Spreadborough }
24505b405d5SPeter Spreadborough }
24605b405d5SPeter Spreadborough }
24705b405d5SPeter Spreadborough
24805b405d5SPeter Spreadborough /*
24905b405d5SPeter Spreadborough * If we have a contender then move it to the new spot.
25005b405d5SPeter Spreadborough */
25105b405d5SPeter Spreadborough if (max) {
25205b405d5SPeter Spreadborough rc = dpool_move(dpool,
25305b405d5SPeter Spreadborough free_list->entry[largest_free_index].index,
25405b405d5SPeter Spreadborough max_index);
25505b405d5SPeter Spreadborough if (rc) {
256adf0802eSRandy Schacher tfp_free(free_list);
257adf0802eSRandy Schacher tfp_free(adj_list);
25805b405d5SPeter Spreadborough return rc;
25905b405d5SPeter Spreadborough }
26005b405d5SPeter Spreadborough } else {
26105b405d5SPeter Spreadborough break;
26205b405d5SPeter Spreadborough }
26305b405d5SPeter Spreadborough }
26405b405d5SPeter Spreadborough
26505b405d5SPeter Spreadborough done:
266adf0802eSRandy Schacher tfp_free(free_list);
267adf0802eSRandy Schacher tfp_free(adj_list);
26805b405d5SPeter Spreadborough return largest_free_size;
26905b405d5SPeter Spreadborough }
27005b405d5SPeter Spreadborough
dpool_alloc(struct dpool * dpool,uint32_t size,uint8_t defrag)27105b405d5SPeter Spreadborough uint32_t dpool_alloc(struct dpool *dpool,
27205b405d5SPeter Spreadborough uint32_t size,
27305b405d5SPeter Spreadborough uint8_t defrag)
27405b405d5SPeter Spreadborough {
27505b405d5SPeter Spreadborough uint32_t i;
27605b405d5SPeter Spreadborough uint32_t j;
27705b405d5SPeter Spreadborough uint32_t count = 0;
27805b405d5SPeter Spreadborough uint32_t first_entry_index;
27905b405d5SPeter Spreadborough int rc;
28005b405d5SPeter Spreadborough
28105b405d5SPeter Spreadborough if (size > dpool->max_alloc_size || size == 0)
28205b405d5SPeter Spreadborough return DP_INVALID_INDEX;
28305b405d5SPeter Spreadborough
28405b405d5SPeter Spreadborough /*
28505b405d5SPeter Spreadborough * Defrag requires EM move support.
28605b405d5SPeter Spreadborough */
28705b405d5SPeter Spreadborough if (defrag != DP_DEFRAG_NONE &&
28805b405d5SPeter Spreadborough dpool->move_callback == NULL)
28905b405d5SPeter Spreadborough return DP_INVALID_INDEX;
29005b405d5SPeter Spreadborough
29105b405d5SPeter Spreadborough while (1) {
29205b405d5SPeter Spreadborough /*
29305b405d5SPeter Spreadborough * find <size> consecutive free entries
29405b405d5SPeter Spreadborough */
29505b405d5SPeter Spreadborough for (i = 0; i < dpool->size; i++) {
29605b405d5SPeter Spreadborough if (DP_IS_FREE(dpool->entry[i].flags)) {
29705b405d5SPeter Spreadborough if (count == 0)
29805b405d5SPeter Spreadborough first_entry_index = i;
29905b405d5SPeter Spreadborough
30005b405d5SPeter Spreadborough count++;
30105b405d5SPeter Spreadborough
30205b405d5SPeter Spreadborough if (count == size) {
30305b405d5SPeter Spreadborough for (j = 0; j < size; j++) {
30405b405d5SPeter Spreadborough dpool->entry[j + first_entry_index].flags = size;
30505b405d5SPeter Spreadborough if (j == 0)
30605b405d5SPeter Spreadborough dpool->entry[j + first_entry_index].flags |=
30705b405d5SPeter Spreadborough DP_FLAGS_START;
30805b405d5SPeter Spreadborough }
30905b405d5SPeter Spreadborough
31005b405d5SPeter Spreadborough dpool->entry[i].entry_data = 0UL;
31105b405d5SPeter Spreadborough return (first_entry_index + dpool->start_index);
31205b405d5SPeter Spreadborough }
31305b405d5SPeter Spreadborough } else {
31405b405d5SPeter Spreadborough count = 0;
31505b405d5SPeter Spreadborough }
31605b405d5SPeter Spreadborough }
31705b405d5SPeter Spreadborough
31805b405d5SPeter Spreadborough /*
31905b405d5SPeter Spreadborough * If defragging then do it to it
32005b405d5SPeter Spreadborough */
32105b405d5SPeter Spreadborough if (defrag != DP_DEFRAG_NONE) {
32205b405d5SPeter Spreadborough rc = dpool_defrag(dpool, size, defrag);
32305b405d5SPeter Spreadborough
32405b405d5SPeter Spreadborough if (rc < 0)
32505b405d5SPeter Spreadborough return DP_INVALID_INDEX;
32605b405d5SPeter Spreadborough } else {
32705b405d5SPeter Spreadborough break;
32805b405d5SPeter Spreadborough }
32905b405d5SPeter Spreadborough
33005b405d5SPeter Spreadborough /*
33105b405d5SPeter Spreadborough * If the defrag created enough space then try the
33205b405d5SPeter Spreadborough * alloc again else quit.
33305b405d5SPeter Spreadborough */
33405b405d5SPeter Spreadborough if ((uint32_t)rc < size)
33505b405d5SPeter Spreadborough break;
33605b405d5SPeter Spreadborough }
33705b405d5SPeter Spreadborough
33805b405d5SPeter Spreadborough return DP_INVALID_INDEX;
33905b405d5SPeter Spreadborough }
34005b405d5SPeter Spreadborough
dpool_free(struct dpool * dpool,uint32_t index)34105b405d5SPeter Spreadborough int dpool_free(struct dpool *dpool,
34205b405d5SPeter Spreadborough uint32_t index)
34305b405d5SPeter Spreadborough {
34405b405d5SPeter Spreadborough uint32_t i;
34505b405d5SPeter Spreadborough int start = (index - dpool->start_index);
34605b405d5SPeter Spreadborough uint32_t size;
34705b405d5SPeter Spreadborough
34805b405d5SPeter Spreadborough if (start < 0)
34905b405d5SPeter Spreadborough return -1;
35005b405d5SPeter Spreadborough
35105b405d5SPeter Spreadborough if (DP_IS_START(dpool->entry[start].flags)) {
35205b405d5SPeter Spreadborough size = DP_FLAGS_SIZE(dpool->entry[start].flags);
35305b405d5SPeter Spreadborough if (size > dpool->max_alloc_size || size == 0)
35405b405d5SPeter Spreadborough return -1;
35505b405d5SPeter Spreadborough
35605b405d5SPeter Spreadborough for (i = start; i < (start + size); i++)
35705b405d5SPeter Spreadborough dpool->entry[i].flags = 0;
35805b405d5SPeter Spreadborough
35905b405d5SPeter Spreadborough return 0;
36005b405d5SPeter Spreadborough }
36105b405d5SPeter Spreadborough
36205b405d5SPeter Spreadborough return -1;
36305b405d5SPeter Spreadborough }
36405b405d5SPeter Spreadborough
dpool_free_all(struct dpool * dpool)36505b405d5SPeter Spreadborough void dpool_free_all(struct dpool *dpool)
36605b405d5SPeter Spreadborough {
36705b405d5SPeter Spreadborough uint32_t i;
36805b405d5SPeter Spreadborough
36905b405d5SPeter Spreadborough for (i = 0; i < dpool->size; i++)
37005b405d5SPeter Spreadborough dpool_free(dpool, dpool->entry[i].index);
37105b405d5SPeter Spreadborough }
37205b405d5SPeter Spreadborough
dpool_set_entry_data(struct dpool * dpool,uint32_t index,uint64_t entry_data)37305b405d5SPeter Spreadborough int dpool_set_entry_data(struct dpool *dpool,
37405b405d5SPeter Spreadborough uint32_t index,
37505b405d5SPeter Spreadborough uint64_t entry_data)
37605b405d5SPeter Spreadborough {
37705b405d5SPeter Spreadborough int start = (index - dpool->start_index);
37805b405d5SPeter Spreadborough
37905b405d5SPeter Spreadborough if (start < 0)
38005b405d5SPeter Spreadborough return -1;
38105b405d5SPeter Spreadborough
38205b405d5SPeter Spreadborough if (DP_IS_START(dpool->entry[start].flags)) {
38305b405d5SPeter Spreadborough dpool->entry[start].entry_data = entry_data;
38405b405d5SPeter Spreadborough return 0;
38505b405d5SPeter Spreadborough }
38605b405d5SPeter Spreadborough
38705b405d5SPeter Spreadborough return -1;
38805b405d5SPeter Spreadborough }
389