On 26/12/2021 22:24, Arunpravin wrote:
On contiguous allocation, we round up the size
to the *next* power of 2, implement a function
to free the unused pages after the newly allocate block.
v2(Matthew Auld):
- replace function name 'drm_buddy_free_unused_pages' with
drm_buddy_block_trim
- replace input argument name 'actual_size' with 'new_size'
- add more validation checks for input arguments
- add overlaps check to avoid needless searching and splitting
- merged the below patch to see the feature in action
- add free unused pages support to i915 driver
- lock drm_buddy_block_trim() function as it calls mark_free/mark_split
are all globally visible
v3(Matthew Auld):
- remove trim method error handling as we address the failure case
at drm_buddy_block_trim() function
v4:
- in case of trim, at __alloc_range() split_block failure path
marks the block as free and removes it from the original list,
potentially also freeing it, to overcome this problem, we turn
the drm_buddy_block_trim() input node into a temporary node to
prevent recursively freeing itself, but still retain the
un-splitting/freeing of the other nodes(Matthew Auld)
- modify the drm_buddy_block_trim() function return type
Signed-off-by: Arunpravin <Arunpravin.PaneerSelvam@xxxxxxx>
---
drivers/gpu/drm/drm_buddy.c | 61 +++++++++++++++++++
drivers/gpu/drm/i915/i915_ttm_buddy_manager.c | 8 +++
include/drm/drm_buddy.h | 4 ++
3 files changed, 73 insertions(+)
diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c
index eddc1eeda02e..855afcaf7edd 100644
--- a/drivers/gpu/drm/drm_buddy.c
+++ b/drivers/gpu/drm/drm_buddy.c
@@ -538,6 +538,67 @@ static int __drm_buddy_alloc_range(struct drm_buddy_mm *mm,
return __alloc_range(mm, &dfs, start, size, blocks);
}
+/**
+ * drm_buddy_block_trim - free unused pages
+ *
+ * @mm: DRM buddy manager
+ * @new_size: original size requested
+ * @blocks: output list head to add allocated blocks
+ *
+ * For contiguous allocation, we round up the size to the nearest
+ * power of two value, drivers consume *actual* size, so remaining
+ * portions are unused and it can be freed.
+ */
+void drm_buddy_block_trim(struct drm_buddy_mm *mm,
+ u64 new_size,
+ struct list_head *blocks)
It might be better to just return the error, and let the user decide if
they want to ignore it? Also we might want some kind of unit test for
this, so having an actual return value might be useful there.
+{
+ struct drm_buddy_block *parent;
+ struct drm_buddy_block *block;
+ LIST_HEAD(dfs);
+ u64 new_start;
+ int err;
+
+ if (!list_is_singular(blocks))
+ return;
+
+ block = list_first_entry(blocks,
+ struct drm_buddy_block,
+ link);
+
+ if (!drm_buddy_block_is_allocated(block))
+ return;
+
+ if (new_size > drm_buddy_block_size(mm, block))
+ return;
+
+ if (!new_size && !IS_ALIGNED(new_size, mm->chunk_size))
+ return;
+
+ if (new_size == drm_buddy_block_size(mm, block))
+ return;
+
+ list_del(&block->link);
+ mark_free(mm, block);
+ mm->avail += drm_buddy_block_size(mm, block);
+
+ /* Prevent recursively freeing this node */
+ parent = block->parent;
+ block->parent = NULL;
+
+ new_start = drm_buddy_block_offset(block);
+ list_add(&block->tmp_link, &dfs);
+ err = __alloc_range(mm, &dfs, new_start, new_size, blocks);
+ if (err) {
+ mark_allocated(block);
+ mm->avail -= drm_buddy_block_size(mm, block);
+ list_add(&block->link, blocks);
+ }
+
+ block->parent = parent;
+}
+EXPORT_SYMBOL(drm_buddy_block_trim);
+
/**
* drm_buddy_alloc - allocate power-of-two blocks
*
diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
index 7c58efb60dba..05f924f32e96 100644
--- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
+++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
@@ -97,6 +97,14 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
if (unlikely(err))
goto err_free_blocks;
+ if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
+ mutex_lock(&bman->lock);
+ drm_buddy_block_trim(mm,
+ (u64)n_pages << PAGE_SHIFT,
AFAIK, n_pages has already been rounded up to the next power-of-two
here, so this becomes a noop. I assume we need to use the "original"
size here?
+ &bman_res->blocks);
+ mutex_unlock(&bman->lock);
+ }
+
*res = &bman_res->base;
return 0;
diff --git a/include/drm/drm_buddy.h b/include/drm/drm_buddy.h
index f573b02304f4..703866a87939 100644
--- a/include/drm/drm_buddy.h
+++ b/include/drm/drm_buddy.h
@@ -146,6 +146,10 @@ int drm_buddy_alloc(struct drm_buddy_mm *mm,
struct list_head *blocks,
unsigned long flags);
+void drm_buddy_block_trim(struct drm_buddy_mm *mm,
+ u64 new_size,
+ struct list_head *blocks);
+
void drm_buddy_free(struct drm_buddy_mm *mm, struct drm_buddy_block *block);
void drm_buddy_free_list(struct drm_buddy_mm *mm, struct list_head *objects);