summaryrefslogtreecommitdiff
path: root/drivers/gpu/buddy.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/buddy.c')
-rw-r--r--drivers/gpu/buddy.c107
1 files changed, 66 insertions, 41 deletions
diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
index b12d3a2ac630..3c73ae87f3c5 100644
--- a/drivers/gpu/buddy.c
+++ b/drivers/gpu/buddy.c
@@ -193,6 +193,9 @@ static void mark_allocated(struct gpu_buddy *mm,
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_ALLOCATED;
+ mm->free_scoreboard[gpu_buddy_block_order(block)]--;
+ mm->used_scoreboard[gpu_buddy_block_order(block)]++;
+
rbtree_remove(mm, block);
}
@@ -201,9 +204,14 @@ static void mark_free(struct gpu_buddy *mm,
{
enum gpu_buddy_free_tree tree;
+ if (gpu_buddy_block_is_allocated(block))
+ mm->used_scoreboard[gpu_buddy_block_order(block)]--;
+
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_FREE;
+ mm->free_scoreboard[gpu_buddy_block_order(block)]++;
+
tree = get_block_tree(block);
rbtree_insert(mm, block, tree);
}
@@ -214,6 +222,8 @@ static void mark_split(struct gpu_buddy *mm,
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_SPLIT;
+ mm->free_scoreboard[gpu_buddy_block_order(block)]--;
+
rbtree_remove(mm, block);
}
@@ -271,9 +281,13 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm,
}
rbtree_remove(mm, buddy);
+ mm->free_scoreboard[gpu_buddy_block_order(buddy)]--;
if (force_merge && gpu_buddy_block_is_clear(buddy))
mm->clear_avail -= gpu_buddy_block_size(mm, buddy);
+ if (gpu_buddy_block_is_allocated(block))
+ mm->used_scoreboard[gpu_buddy_block_order(block)]--;
+
gpu_block_free(mm, block);
gpu_block_free(mm, buddy);
@@ -335,6 +349,7 @@ static int __force_merge(struct gpu_buddy *mm,
iter = rb_prev(iter);
rbtree_remove(mm, block);
+ mm->free_scoreboard[gpu_buddy_block_order(block)]--;
if (gpu_buddy_block_is_clear(block))
mm->clear_avail -= gpu_buddy_block_size(mm, block);
@@ -384,11 +399,23 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size)
BUG_ON(mm->max_order > GPU_BUDDY_MAX_ORDER);
+ mm->free_scoreboard = kcalloc(mm->max_order + 1,
+ sizeof(*mm->free_scoreboard),
+ GFP_KERNEL);
+ if (!mm->free_scoreboard)
+ return -ENOMEM;
+
+ mm->used_scoreboard = kcalloc(mm->max_order + 1,
+ sizeof(*mm->used_scoreboard),
+ GFP_KERNEL);
+ if (!mm->used_scoreboard)
+ goto out_free_free_scoreboard;
+
mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES,
sizeof(*mm->free_trees),
GFP_KERNEL);
if (!mm->free_trees)
- return -ENOMEM;
+ goto out_free_used_scoreboard;
for_each_free_tree(i) {
mm->free_trees[i] = kmalloc_array(mm->max_order + 1,
@@ -450,6 +477,10 @@ out_free_tree:
while (i--)
kfree(mm->free_trees[i]);
kfree(mm->free_trees);
+out_free_used_scoreboard:
+ kfree(mm->used_scoreboard);
+out_free_free_scoreboard:
+ kfree(mm->free_scoreboard);
return -ENOMEM;
}
EXPORT_SYMBOL(gpu_buddy_init);
@@ -484,10 +515,15 @@ void gpu_buddy_fini(struct gpu_buddy *mm)
gpu_buddy_assert(mm->avail == mm->size);
+ for (i = 0; i <= mm->max_order; ++i)
+ gpu_buddy_assert(!mm->used_scoreboard[i]);
+
for_each_free_tree(i)
kfree(mm->free_trees[i]);
kfree(mm->free_trees);
kfree(mm->roots);
+ kfree(mm->free_scoreboard);
+ kfree(mm->used_scoreboard);
}
EXPORT_SYMBOL(gpu_buddy_fini);
@@ -650,6 +686,20 @@ static bool block_incompatible(struct gpu_buddy_block *block, unsigned int flags
return needs_clear != gpu_buddy_block_is_clear(block);
}
+static void __gpu_buddy_undo_splits(struct gpu_buddy *mm,
+ struct gpu_buddy_block *block)
+{
+ struct gpu_buddy_block *buddy = __get_buddy(block);
+
+ if (buddy &&
+ (gpu_buddy_block_is_free(block) &&
+ gpu_buddy_block_is_free(buddy))) {
+ rbtree_remove(mm, block);
+ mm->free_scoreboard[gpu_buddy_block_order(block)]--;
+ __gpu_buddy_free(mm, block, false);
+ }
+}
+
static struct gpu_buddy_block *
__alloc_range_bias(struct gpu_buddy *mm,
u64 start, u64 end,
@@ -659,7 +709,6 @@ __alloc_range_bias(struct gpu_buddy *mm,
{
u64 req_size = mm->chunk_size << order;
struct gpu_buddy_block *block;
- struct gpu_buddy_block *buddy;
LIST_HEAD(dfs);
int err;
int i;
@@ -734,11 +783,7 @@ err_undo:
* bigger is better, so make sure we merge everything back before we
* free the allocated blocks.
*/
- buddy = __get_buddy(block);
- if (buddy &&
- (gpu_buddy_block_is_free(block) &&
- gpu_buddy_block_is_free(buddy)))
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_undo_splits(mm, block);
return ERR_PTR(err);
}
@@ -847,8 +892,7 @@ alloc_from_freetree(struct gpu_buddy *mm,
return block;
err_undo:
- if (tmp != order)
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_undo_splits(mm, block);
return ERR_PTR(err);
}
@@ -912,7 +956,6 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm,
{
struct gpu_buddy_block *block = NULL;
unsigned int order, tmp, alignment;
- struct gpu_buddy_block *buddy;
enum gpu_buddy_free_tree tree;
unsigned long pages;
int err;
@@ -965,11 +1008,7 @@ err_undo:
* bigger is better, so make sure we merge everything back before we
* free the allocated blocks.
*/
- buddy = __get_buddy(block);
- if (buddy &&
- (gpu_buddy_block_is_free(block) &&
- gpu_buddy_block_is_free(buddy)))
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_undo_splits(mm, block);
return ERR_PTR(err);
}
@@ -980,7 +1019,6 @@ static int __alloc_range(struct gpu_buddy *mm,
u64 *total_allocated_on_err)
{
struct gpu_buddy_block *block;
- struct gpu_buddy_block *buddy;
u64 total_allocated = 0;
LIST_HEAD(allocated);
u64 end;
@@ -1051,11 +1089,7 @@ err_undo:
* bigger is better, so make sure we merge everything back before we
* free the allocated blocks.
*/
- buddy = __get_buddy(block);
- if (buddy &&
- (gpu_buddy_block_is_free(block) &&
- gpu_buddy_block_is_free(buddy)))
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_undo_splits(mm, block);
err_free:
if (err == -ENOSPC && total_allocated_on_err) {
@@ -1515,27 +1549,18 @@ void gpu_buddy_print(struct gpu_buddy *mm)
mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
for (order = mm->max_order; order >= 0; order--) {
- struct gpu_buddy_block *block, *tmp;
- struct rb_root *root;
- u64 count = 0, free;
- unsigned int tree;
-
- for_each_free_tree(tree) {
- root = &mm->free_trees[tree][order];
-
- rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
- BUG_ON(!gpu_buddy_block_is_free(block));
- count++;
- }
- }
-
- free = count * (mm->chunk_size << order);
- if (free < SZ_1M)
- pr_info("order-%2d free: %8llu KiB, blocks: %llu\n",
- order, free >> 10, count);
+ u64 free_count = mm->free_scoreboard[order];
+ u64 used_count = mm->used_scoreboard[order];
+ u64 block_size = mm->chunk_size << order;
+ u64 free = free_count * block_size;
+ u64 used = used_count * block_size;
+
+ if (block_size < SZ_1M)
+ pr_info("order-%2d free: %8llu KiB, used: %8llu KiB, free_blocks: %llu, used_blocks: %llu\n",
+ order, free >> 10, used >> 10, free_count, used_count);
else
- pr_info("order-%2d free: %8llu MiB, blocks: %llu\n",
- order, free >> 20, count);
+ pr_info("order-%2d free: %8llu MiB, used: %8llu MiB, free_blocks: %llu, used_blocks: %llu\n",
+ order, free >> 20, used >> 20, free_count, used_count);
}
}
EXPORT_SYMBOL(gpu_buddy_print);