Add a function to support defragmentation. Signed-off-by: Arunpravin Paneer Selvam <Arunpravin.PaneerSelvam@xxxxxxx> Suggested-by: Matthew Auld <matthew.auld@xxxxxxxxx> --- drivers/gpu/drm/drm_buddy.c | 48 ++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index d44172f23f05..3cffa9cc12d7 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -277,7 +277,8 @@ drm_get_buddy(struct drm_buddy_block *block) EXPORT_SYMBOL(drm_get_buddy); static void __drm_buddy_free(struct drm_buddy *mm, - struct drm_buddy_block *block) + struct drm_buddy_block *block, + bool defrag) { struct drm_buddy_block *parent; @@ -289,12 +290,14 @@ static void __drm_buddy_free(struct drm_buddy *mm, if (!drm_buddy_block_is_free(buddy)) break; - if (drm_buddy_block_is_clear(block) != - drm_buddy_block_is_clear(buddy)) - break; + if (!defrag) { + if (drm_buddy_block_is_clear(block) != + drm_buddy_block_is_clear(buddy)) + break; - if (drm_buddy_block_is_clear(block)) - mark_cleared(parent); + if (drm_buddy_block_is_clear(block)) + mark_cleared(parent); + } list_del(&buddy->link); @@ -307,6 +310,19 @@ static void __drm_buddy_free(struct drm_buddy *mm, mark_free(mm, block); } +static void drm_buddy_defrag(struct drm_buddy *mm) +{ + struct drm_buddy_block *block; + struct list_head *list; + + list = &mm->free_list[0]; + if (list_empty(list)) + return; + + list_for_each_entry_reverse(block, list, link) + __drm_buddy_free(mm, block, 1); +} + /** * drm_buddy_free_block - free a block * @@ -321,7 +337,7 @@ void drm_buddy_free_block(struct drm_buddy *mm, if (drm_buddy_block_is_clear(block)) mm->clear_avail += drm_buddy_block_size(mm, block); - __drm_buddy_free(mm, block); + __drm_buddy_free(mm, block, 0); } EXPORT_SYMBOL(drm_buddy_free_block); @@ -447,7 +463,7 @@ __alloc_range_bias(struct drm_buddy *mm, if (buddy && (drm_buddy_block_is_free(block) && drm_buddy_block_is_free(buddy))) - __drm_buddy_free(mm, block); + __drm_buddy_free(mm, block, 0); return ERR_PTR(err); } @@ -577,7 +593,7 @@ alloc_from_freelist(struct drm_buddy *mm, err_undo: if (tmp != order) - __drm_buddy_free(mm, block); + __drm_buddy_free(mm, block, 0); return ERR_PTR(err); } @@ -657,7 +673,7 @@ static int __alloc_range(struct drm_buddy *mm, if (buddy && (drm_buddy_block_is_free(block) && drm_buddy_block_is_free(buddy))) - __drm_buddy_free(mm, block); + __drm_buddy_free(mm, block, 0); err_free: if (err == -ENOSPC && total_allocated_on_err) { @@ -903,7 +919,7 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm, if (order-- == min_order) { if (flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION && - !(flags & DRM_BUDDY_RANGE_ALLOCATION)) + !(flags & DRM_BUDDY_RANGE_ALLOCATION)) { /* * Try contiguous block allocation through * try harder method @@ -912,6 +928,16 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm, original_size, original_min_size, blocks); + + /* + * Defragment the freelist and try contiguous block + * allocation + */ + drm_buddy_defrag(mm); + if (!IS_ERR(alloc_from_freelist(mm, min_order, flags))) + break; + } + err = -ENOSPC; goto err_free; } -- 2.25.1