[PATCH v9 1/3] drm/buddy: Implement tracking clear page feature

Matthew Auld matthew.auld at intel.com
Fri Apr 5 15:43:02 UTC 2024


On 01/04/2024 12:07, Paneer Selvam, Arunpravin wrote:
> Hi Matthew,
> 
> On 3/28/2024 10:18 PM, Matthew Auld wrote:
>> On 28/03/2024 16:07, Paneer Selvam, Arunpravin wrote:
>>> Hi Matthew,
>>>
>>> On 3/26/2024 11:39 PM, Matthew Auld wrote:
>>>> On 18/03/2024 21:40, Arunpravin Paneer Selvam wrote:
>>>>> - Add tracking clear page feature.
>>>>>
>>>>> - Driver should enable the DRM_BUDDY_CLEARED flag if it
>>>>>    successfully clears the blocks in the free path. On the otherhand,
>>>>>    DRM buddy marks each block as cleared.
>>>>>
>>>>> - Track the available cleared pages size
>>>>>
>>>>> - If driver requests cleared memory we prefer cleared memory
>>>>>    but fallback to uncleared if we can't find the cleared blocks.
>>>>>    when driver requests uncleared memory we try to use uncleared but
>>>>>    fallback to cleared memory if necessary.
>>>>>
>>>>> - When a block gets freed we clear it and mark the freed block as 
>>>>> cleared,
>>>>>    when there are buddies which are cleared as well we can merge them.
>>>>>    Otherwise, we prefer to keep the blocks as separated.
>>>>>
>>>>> - Add a function to support defragmentation.
>>>>>
>>>>> v1:
>>>>>    - Depends on the flag check DRM_BUDDY_CLEARED, enable the block as
>>>>>      cleared. Else, reset the clear flag for each block in the 
>>>>> list(Christian)
>>>>>    - For merging the 2 cleared blocks compare as below,
>>>>>      drm_buddy_is_clear(block) != drm_buddy_is_clear(buddy)(Christian)
>>>>>    - Defragment the memory beginning from min_order
>>>>>      till the required memory space is available.
>>>>>
>>>>> v2: (Matthew)
>>>>>    - Add a wrapper drm_buddy_free_list_internal for the freeing of 
>>>>> blocks
>>>>>      operation within drm buddy.
>>>>>    - Write a macro block_incompatible() to allocate the required 
>>>>> blocks.
>>>>>    - Update the xe driver for the drm_buddy_free_list change in 
>>>>> arguments.
>>>>>    - add a warning if the two blocks are incompatible on
>>>>>      defragmentation
>>>>>    - call full defragmentation in the fini() function
>>>>>    - place a condition to test if min_order is equal to 0
>>>>>    - replace the list with safe_reverse() variant as we might
>>>>>      remove the block from the list.
>>>>>
>>>>> v3:
>>>>>    - fix Gitlab user reported lockup issue.
>>>>>    - Keep DRM_BUDDY_HEADER_CLEAR define sorted(Matthew)
>>>>>    - modify to pass the root order instead max_order in fini()
>>>>>      function(Matthew)
>>>>>    - change bool 1 to true(Matthew)
>>>>>    - add check if min_block_size is power of 2(Matthew)
>>>>>    - modify the min_block_size datatype to u64(Matthew)
>>>>>
>>>>> v4:
>>>>>    - rename the function drm_buddy_defrag with __force_merge.
>>>>>    - Include __force_merge directly in drm buddy file and remove
>>>>>      the defrag use in amdgpu driver.
>>>>>    - Remove list_empty() check(Matthew)
>>>>>    - Remove unnecessary space, headers and placement of new 
>>>>> variables(Matthew)
>>>>>    - Add a unit test case(Matthew)
>>>>>
>>>>> Signed-off-by: Arunpravin Paneer Selvam 
>>>>> <Arunpravin.PaneerSelvam at amd.com>
>>>>> Signed-off-by: Matthew Auld <matthew.auld at intel.com>
>>>>> Suggested-by: Christian König <christian.koenig at amd.com>
>>>>> Suggested-by: Matthew Auld <matthew.auld at intel.com>
>>>>> ---
>>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c  |   6 +-
>>>>>   drivers/gpu/drm/drm_buddy.c                   | 427 
>>>>> ++++++++++++++----
>>>>>   drivers/gpu/drm/i915/i915_ttm_buddy_manager.c |   6 +-
>>>>>   drivers/gpu/drm/tests/drm_buddy_test.c        |  18 +-
>>>>>   drivers/gpu/drm/xe/xe_ttm_vram_mgr.c          |   4 +-
>>>>>   include/drm/drm_buddy.h                       |  16 +-
>>>>>   6 files changed, 360 insertions(+), 117 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c 
>>>>> b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
>>>>> index 8db880244324..c0c851409241 100644
>>>>> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
>>>>> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
>>>>> @@ -571,7 +571,7 @@ static int amdgpu_vram_mgr_new(struct 
>>>>> ttm_resource_manager *man,
>>>>>       return 0;
>>>>>     error_free_blocks:
>>>>> -    drm_buddy_free_list(mm, &vres->blocks);
>>>>> +    drm_buddy_free_list(mm, &vres->blocks, 0);
>>>>>       mutex_unlock(&mgr->lock);
>>>>>   error_fini:
>>>>>       ttm_resource_fini(man, &vres->base);
>>>>> @@ -604,7 +604,7 @@ static void amdgpu_vram_mgr_del(struct 
>>>>> ttm_resource_manager *man,
>>>>>         amdgpu_vram_mgr_do_reserve(man);
>>>>>   -    drm_buddy_free_list(mm, &vres->blocks);
>>>>> +    drm_buddy_free_list(mm, &vres->blocks, 0);
>>>>>       mutex_unlock(&mgr->lock);
>>>>>         atomic64_sub(vis_usage, &mgr->vis_usage);
>>>>> @@ -912,7 +912,7 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device 
>>>>> *adev)
>>>>>           kfree(rsv);
>>>>>         list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, 
>>>>> blocks) {
>>>>> -        drm_buddy_free_list(&mgr->mm, &rsv->allocated);
>>>>> +        drm_buddy_free_list(&mgr->mm, &rsv->allocated, 0);
>>>>>           kfree(rsv);
>>>>>       }
>>>>>       if (!adev->gmc.is_app_apu)
>>>>> diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c
>>>>> index c4222b886db7..625a30a6b855 100644
>>>>> --- a/drivers/gpu/drm/drm_buddy.c
>>>>> +++ b/drivers/gpu/drm/drm_buddy.c
>>>>> @@ -38,8 +38,8 @@ static void drm_block_free(struct drm_buddy *mm,
>>>>>       kmem_cache_free(slab_blocks, block);
>>>>>   }
>>>>>   -static void list_insert_sorted(struct drm_buddy *mm,
>>>>> -                   struct drm_buddy_block *block)
>>>>> +static void list_insert(struct drm_buddy *mm,
>>>>> +            struct drm_buddy_block *block)
>>>>>   {
>>>>>       struct drm_buddy_block *node;
>>>>>       struct list_head *head;
>>>>> @@ -57,6 +57,16 @@ static void list_insert_sorted(struct drm_buddy 
>>>>> *mm,
>>>>>       __list_add(&block->link, node->link.prev, &node->link);
>>>>>   }
>>>>>   +static void clear_reset(struct drm_buddy_block *block)
>>>>> +{
>>>>> +    block->header &= ~DRM_BUDDY_HEADER_CLEAR;
>>>>> +}
>>>>> +
>>>>> +static void mark_cleared(struct drm_buddy_block *block)
>>>>> +{
>>>>> +    block->header |= DRM_BUDDY_HEADER_CLEAR;
>>>>> +}
>>>>> +
>>>>>   static void mark_allocated(struct drm_buddy_block *block)
>>>>>   {
>>>>>       block->header &= ~DRM_BUDDY_HEADER_STATE;
>>>>> @@ -71,7 +81,7 @@ static void mark_free(struct drm_buddy *mm,
>>>>>       block->header &= ~DRM_BUDDY_HEADER_STATE;
>>>>>       block->header |= DRM_BUDDY_FREE;
>>>>>   -    list_insert_sorted(mm, block);
>>>>> +    list_insert(mm, block);
>>>>>   }
>>>>>     static void mark_split(struct drm_buddy_block *block)
>>>>> @@ -82,6 +92,114 @@ static void mark_split(struct drm_buddy_block 
>>>>> *block)
>>>>>       list_del(&block->link);
>>>>>   }
>>>>>   +static struct drm_buddy_block *
>>>>> +__get_buddy(struct drm_buddy_block *block)
>>>>> +{
>>>>> +    struct drm_buddy_block *parent;
>>>>> +
>>>>> +    parent = block->parent;
>>>>> +    if (!parent)
>>>>> +        return NULL;
>>>>> +
>>>>> +    if (parent->left == block)
>>>>> +        return parent->right;
>>>>> +
>>>>> +    return parent->left;
>>>>> +}
>>>>> +
>>>>> +static unsigned int __drm_buddy_free(struct drm_buddy *mm,
>>>>> +                     struct drm_buddy_block *block,
>>>>> +                     bool force_merge)
>>>>> +{
>>>>> +    struct drm_buddy_block *parent;
>>>>> +    unsigned int order;
>>>>> +
>>>>> +    while ((parent = block->parent)) {
>>>>> +        struct drm_buddy_block *buddy;
>>>>> +
>>>>> +        buddy = __get_buddy(block);
>>>>> +
>>>>> +        if (!drm_buddy_block_is_free(buddy))
>>>>> +            break;
>>>>> +
>>>>> +        if (!force_merge) {
>>>>> +            /**
>>>>
>>>> Not really valid kernel-doc AFAIK. I think drop the extra *. Below 
>>>> also.
>>>>
>>>>> +             * Check the block and its buddy clear state and exit
>>>>> +             * the loop if they both have the dissimilar state.
>>>>> +             */
>>>>> +            if (drm_buddy_block_is_clear(block) !=
>>>>> +                drm_buddy_block_is_clear(buddy))
>>>>> +                break;
>>>>> +
>>>>> +            if (drm_buddy_block_is_clear(block))
>>>>> +                mark_cleared(parent);
>>>>> +        }
>>>>> +
>>>>> +        list_del(&buddy->link);
>>>>> +        if (force_merge && drm_buddy_block_is_clear(buddy))
>>>>> +            mm->clear_avail -= drm_buddy_block_size(mm, buddy);
>>>>> +
>>>>> +        drm_block_free(mm, block);
>>>>> +        drm_block_free(mm, buddy);
>>>>> +
>>>>> +        block = parent;
>>>>> +    }
>>>>> +
>>>>> +    order = drm_buddy_block_order(block);
>>>>> +    mark_free(mm, block);
>>>>> +
>>>>> +    return order;
>>>>> +}
>>>>> +
>>>>> +static int __force_merge(struct drm_buddy *mm,
>>>>> +             unsigned int min_order)
>>>>> +{
>>>>> +    unsigned int order;
>>>>> +    int i;
>>>>> +
>>>>> +    if (!min_order)
>>>>> +        return -ENOMEM;
>>>>> +
>>>>> +    if (min_order > mm->max_order)
>>>>> +        return -EINVAL;
>>>>> +
>>>>> +    for (i = min_order - 1; i >= 0; i--) {
>>>>> +        struct drm_buddy_block *block, *prev;
>>>>> +
>>>>> +        list_for_each_entry_safe_reverse(block, prev, 
>>>>> &mm->free_list[i], link) {
>>>>> +            struct drm_buddy_block *buddy;
>>>>> +
>>>>> +            if (!block->parent)
>>>>> +                continue;
>>>>> +
>>>>> +            buddy = __get_buddy(block);
>>>>> +            if (!drm_buddy_block_is_free(buddy))
>>>>> +                continue;
>>>>> +
>>>>> +            WARN_ON(drm_buddy_block_is_clear(block) ==
>>>>> +                drm_buddy_block_is_clear(buddy));
>>>>> +
>>>>> +            /**
>>>>> +             * If the prev block is same as buddy, don't access the
>>>>> +             * block in the next iteration as we would free the
>>>>> +             * buddy block as part of the free function.
>>>>> +             */
>>>>> +            if (prev == buddy)
>>>>> +                prev = list_prev_entry(prev, link);
>>>>> +
>>>>> +            list_del(&block->link);
>>>>> +            if (drm_buddy_block_is_clear(block))
>>>>> +                mm->clear_avail -= drm_buddy_block_size(mm, block);
>>>>> +
>>>>> +            order = __drm_buddy_free(mm, block, true);
>>>>> +            if (order >= min_order)
>>>>> +                return 0;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    return -ENOMEM;
>>>>> +}
>>>>> +
>>>>>   /**
>>>>>    * drm_buddy_init - init memory manager
>>>>>    *
>>>>> @@ -137,7 +255,7 @@ int drm_buddy_init(struct drm_buddy *mm, u64 
>>>>> size, u64 chunk_size)
>>>>>       offset = 0;
>>>>>       i = 0;
>>>>>   -    /*
>>>>> +    /**
>>>>>        * Split into power-of-two blocks, in case we are given a 
>>>>> size that is
>>>>>        * not itself a power-of-two.
>>>>>        */
>>>>> @@ -186,11 +304,21 @@ EXPORT_SYMBOL(drm_buddy_init);
>>>>>    */
>>>>>   void drm_buddy_fini(struct drm_buddy *mm)
>>>>>   {
>>>>> +    u64 root_size, size;
>>>>> +    unsigned int order;
>>>>>       int i;
>>>>>   +    size = mm->size;
>>>>> +
>>>>>       for (i = 0; i < mm->n_roots; ++i) {
>>>>> +        order = ilog2(size) - ilog2(mm->chunk_size);
>>>>> +        __force_merge(mm, order);
>>>>> +
>>>>> WARN_ON(!drm_buddy_block_is_free(mm->roots[i]));
>>>>>           drm_block_free(mm, mm->roots[i]);
>>>>> +
>>>>> +        root_size = mm->chunk_size << order;
>>>>> +        size -= root_size;
>>>>>       }
>>>>>         WARN_ON(mm->avail != mm->size);
>>>>> @@ -223,26 +351,17 @@ static int split_block(struct drm_buddy *mm,
>>>>>       mark_free(mm, block->left);
>>>>>       mark_free(mm, block->right);
>>>>>   +    if (drm_buddy_block_is_clear(block)) {
>>>>> +        mark_cleared(block->left);
>>>>> +        mark_cleared(block->right);
>>>>> +        clear_reset(block);
>>>>> +    }
>>>>> +
>>>>>       mark_split(block);
>>>>>         return 0;
>>>>>   }
>>>>>   -static struct drm_buddy_block *
>>>>> -__get_buddy(struct drm_buddy_block *block)
>>>>> -{
>>>>> -    struct drm_buddy_block *parent;
>>>>> -
>>>>> -    parent = block->parent;
>>>>> -    if (!parent)
>>>>> -        return NULL;
>>>>> -
>>>>> -    if (parent->left == block)
>>>>> -        return parent->right;
>>>>> -
>>>>> -    return parent->left;
>>>>> -}
>>>>> -
>>>>>   /**
>>>>>    * drm_get_buddy - get buddy address
>>>>>    *
>>>>> @@ -260,30 +379,6 @@ 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 *parent;
>>>>> -
>>>>> -    while ((parent = block->parent)) {
>>>>> -        struct drm_buddy_block *buddy;
>>>>> -
>>>>> -        buddy = __get_buddy(block);
>>>>> -
>>>>> -        if (!drm_buddy_block_is_free(buddy))
>>>>> -            break;
>>>>> -
>>>>> -        list_del(&buddy->link);
>>>>> -
>>>>> -        drm_block_free(mm, block);
>>>>> -        drm_block_free(mm, buddy);
>>>>> -
>>>>> -        block = parent;
>>>>> -    }
>>>>> -
>>>>> -    mark_free(mm, block);
>>>>> -}
>>>>> -
>>>>>   /**
>>>>>    * drm_buddy_free_block - free a block
>>>>>    *
>>>>> @@ -295,26 +390,59 @@ void drm_buddy_free_block(struct drm_buddy *mm,
>>>>>   {
>>>>>       BUG_ON(!drm_buddy_block_is_allocated(block));
>>>>>       mm->avail += drm_buddy_block_size(mm, block);
>>>>> -    __drm_buddy_free(mm, block);
>>>>> +    if (drm_buddy_block_is_clear(block))
>>>>> +        mm->clear_avail += drm_buddy_block_size(mm, block);
>>>>> +
>>>>> +    __drm_buddy_free(mm, block, false);
>>>>>   }
>>>>>   EXPORT_SYMBOL(drm_buddy_free_block);
>>>>>   -/**
>>>>> - * drm_buddy_free_list - free blocks
>>>>> - *
>>>>> - * @mm: DRM buddy manager
>>>>> - * @objects: input list head to free blocks
>>>>> - */
>>>>> -void drm_buddy_free_list(struct drm_buddy *mm, struct list_head 
>>>>> *objects)
>>>>> +static void __drm_buddy_free_list(struct drm_buddy *mm,
>>>>> +                  struct list_head *objects,
>>>>> +                  bool mark_clear,
>>>>> +                  bool mark_dirty)
>>>>>   {
>>>>>       struct drm_buddy_block *block, *on;
>>>>>   +    WARN_ON(mark_dirty && mark_clear);
>>>>> +
>>>>>       list_for_each_entry_safe(block, on, objects, link) {
>>>>> +        if (mark_clear)
>>>>> +            mark_cleared(block);
>>>>> +        else if (mark_dirty)
>>>>> +            clear_reset(block);
>>>>>           drm_buddy_free_block(mm, block);
>>>>>           cond_resched();
>>>>>       }
>>>>>       INIT_LIST_HEAD(objects);
>>>>>   }
>>>>> +
>>>>> +static void drm_buddy_free_list_internal(struct drm_buddy *mm,
>>>>> +                     struct list_head *objects)
>>>>> +{
>>>>> +    /**
>>>>> +     * Don't touch the clear/dirty bit, since allocation is still 
>>>>> internal
>>>>> +     * at this point. For example we might have just failed part 
>>>>> of the
>>>>> +     * allocation.
>>>>> +     */
>>>>> +    __drm_buddy_free_list(mm, objects, false, false);
>>>>> +}
>>>>> +
>>>>> +/**
>>>>> + * drm_buddy_free_list - free blocks
>>>>> + *
>>>>> + * @mm: DRM buddy manager
>>>>> + * @objects: input list head to free blocks
>>>>> + * @flags: optional flags like DRM_BUDDY_CLEARED
>>>>> + */
>>>>> +void drm_buddy_free_list(struct drm_buddy *mm,
>>>>> +             struct list_head *objects,
>>>>> +             unsigned int flags)
>>>>> +{
>>>>> +    bool mark_clear = flags & DRM_BUDDY_CLEARED;
>>>>> +
>>>>> +    __drm_buddy_free_list(mm, objects, mark_clear, !mark_clear);
>>>>> +}
>>>>>   EXPORT_SYMBOL(drm_buddy_free_list);
>>>>>     static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
>>>>> @@ -327,10 +455,19 @@ static inline bool contains(u64 s1, u64 e1, 
>>>>> u64 s2, u64 e2)
>>>>>       return s1 <= s2 && e1 >= e2;
>>>>>   }
>>>>>   +static bool block_incompatible(struct drm_buddy_block *block, 
>>>>> unsigned int flags)
>>>>> +{
>>>>> +    bool needs_clear = flags & DRM_BUDDY_CLEAR_ALLOCATION;
>>>>> +
>>>>> +    return needs_clear != drm_buddy_block_is_clear(block);
>>>>> +}
>>>>> +
>>>>>   static struct drm_buddy_block *
>>>>> -alloc_range_bias(struct drm_buddy *mm,
>>>>> -         u64 start, u64 end,
>>>>> -         unsigned int order)
>>>>> +__alloc_range_bias(struct drm_buddy *mm,
>>>>> +           u64 start, u64 end,
>>>>> +           unsigned int order,
>>>>> +           unsigned long flags,
>>>>> +           bool fallback)
>>>>>   {
>>>>>       struct drm_buddy_block *block;
>>>>>       struct drm_buddy_block *buddy;
>>>>> @@ -369,7 +506,10 @@ alloc_range_bias(struct drm_buddy *mm,
>>>>>             if (contains(start, end, block_start, block_end) &&
>>>>>               order == drm_buddy_block_order(block)) {
>>>>> -            /*
>>>>> +            if (!fallback && block_incompatible(block, flags))
>>>>> +                continue;
>>>>> +
>>>>> +            /**
>>>>>                * Find the free block within the range.
>>>>>                */
>>>>>               if (drm_buddy_block_is_free(block))
>>>>> @@ -391,7 +531,7 @@ alloc_range_bias(struct drm_buddy *mm,
>>>>>       return ERR_PTR(-ENOSPC);
>>>>>     err_undo:
>>>>> -    /*
>>>>> +    /**
>>>>>        * We really don't want to leave around a bunch of split 
>>>>> blocks, since
>>>>>        * bigger is better, so make sure we merge everything back 
>>>>> before we
>>>>>        * free the allocated blocks.
>>>>> @@ -400,30 +540,57 @@ 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, false);
>>>>>       return ERR_PTR(err);
>>>>>   }
>>>>>     static struct drm_buddy_block *
>>>>> -get_maxblock(struct drm_buddy *mm, unsigned int order)
>>>>> +__drm_buddy_alloc_range_bias(struct drm_buddy *mm,
>>>>> +                 u64 start, u64 end,
>>>>> +                 unsigned int order,
>>>>> +                 unsigned long flags)
>>>>> +{
>>>>> +    struct drm_buddy_block *block;
>>>>> +    bool fallback = false;
>>>>> +
>>>>> +    block = __alloc_range_bias(mm, start, end, order,
>>>>> +                   flags, fallback);
>>>>> +    if (IS_ERR(block) && mm->clear_avail)
>>>>> +        return __alloc_range_bias(mm, start, end, order,
>>>>> +                      flags, !fallback);
>>>>> +
>>>>> +    return block;
>>>>> +}
>>>>> +
>>>>> +static struct drm_buddy_block *
>>>>> +get_maxblock(struct drm_buddy *mm, unsigned int order,
>>>>> +         unsigned long flags)
>>>>>   {
>>>>> -    struct drm_buddy_block *max_block = NULL, *node;
>>>>> +    struct drm_buddy_block *max_block = NULL, *block = NULL;
>>>>>       unsigned int i;
>>>>>         for (i = order; i <= mm->max_order; ++i) {
>>>>> -        if (!list_empty(&mm->free_list[i])) {
>>>>> -            node = list_last_entry(&mm->free_list[i],
>>>>> -                           struct drm_buddy_block,
>>>>> -                           link);
>>>>> -            if (!max_block) {
>>>>> -                max_block = node;
>>>>> +        struct drm_buddy_block *tmp_block;
>>>>> +
>>>>> +        list_for_each_entry_reverse(tmp_block, &mm->free_list[i], 
>>>>> link) {
>>>>> +            if (block_incompatible(tmp_block, flags))
>>>>>                   continue;
>>>>> -            }
>>>>>   -            if (drm_buddy_block_offset(node) >
>>>>> -                drm_buddy_block_offset(max_block)) {
>>>>> -                max_block = node;
>>>>> -            }
>>>>> +            block = tmp_block;
>>>>> +            break;
>>>>> +        }
>>>>> +
>>>>> +        if (!block)
>>>>> +            continue;
>>>>> +
>>>>> +        if (!max_block) {
>>>>> +            max_block = block;
>>>>> +            continue;
>>>>> +        }
>>>>> +
>>>>> +        if (drm_buddy_block_offset(block) >
>>>>> +            drm_buddy_block_offset(max_block)) {
>>>>> +            max_block = block;
>>>>>           }
>>>>>       }
>>>>>   @@ -440,11 +607,29 @@ alloc_from_freelist(struct drm_buddy *mm,
>>>>>       int err;
>>>>>         if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) {
>>>>> -        block = get_maxblock(mm, order);
>>>>> +        block = get_maxblock(mm, order, flags);
>>>>>           if (block)
>>>>>               /* Store the obtained block order */
>>>>>               tmp = drm_buddy_block_order(block);
>>>>>       } else {
>>>>> +        for (tmp = order; tmp <= mm->max_order; ++tmp) {
>>>>> +            struct drm_buddy_block *tmp_block;
>>>>> +
>>>>> +            list_for_each_entry_reverse(tmp_block, 
>>>>> &mm->free_list[tmp], link) {
>>>>> +                if (block_incompatible(tmp_block, flags))
>>>>> +                    continue;
>>>>> +
>>>>> +                block = tmp_block;
>>>>> +                break;
>>>>> +            }
>>>>> +
>>>>> +            if (block)
>>>>> +                break;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    if (!block) {
>>>>> +        /* Fallback method */
>>>>>           for (tmp = order; tmp <= mm->max_order; ++tmp) {
>>>>>               if (!list_empty(&mm->free_list[tmp])) {
>>>>>                   block = list_last_entry(&mm->free_list[tmp],
>>>>> @@ -454,10 +639,10 @@ alloc_from_freelist(struct drm_buddy *mm,
>>>>>                       break;
>>>>>               }
>>>>>           }
>>>>> -    }
>>>>>   -    if (!block)
>>>>> -        return ERR_PTR(-ENOSPC);
>>>>> +        if (!block)
>>>>> +            return ERR_PTR(-ENOSPC);
>>>>> +    }
>>>>>         BUG_ON(!drm_buddy_block_is_free(block));
>>>>>   @@ -473,7 +658,7 @@ alloc_from_freelist(struct drm_buddy *mm,
>>>>>     err_undo:
>>>>>       if (tmp != order)
>>>>> -        __drm_buddy_free(mm, block);
>>>>> +        __drm_buddy_free(mm, block, false);
>>>>>       return ERR_PTR(err);
>>>>>   }
>>>>>   @@ -524,6 +709,8 @@ static int __alloc_range(struct drm_buddy *mm,
>>>>>               mark_allocated(block);
>>>>>               total_allocated += drm_buddy_block_size(mm, block);
>>>>>               mm->avail -= drm_buddy_block_size(mm, block);
>>>>> +            if (drm_buddy_block_is_clear(block))
>>>>> +                mm->clear_avail -= drm_buddy_block_size(mm, block);
>>>>>               list_add_tail(&block->link, &allocated);
>>>>>               continue;
>>>>>           }
>>>>> @@ -548,7 +735,7 @@ static int __alloc_range(struct drm_buddy *mm,
>>>>>       return 0;
>>>>>     err_undo:
>>>>> -    /*
>>>>> +    /**
>>>>>        * We really don't want to leave around a bunch of split 
>>>>> blocks, since
>>>>>        * bigger is better, so make sure we merge everything back 
>>>>> before we
>>>>>        * free the allocated blocks.
>>>>> @@ -557,14 +744,14 @@ 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, false);
>>>>>     err_free:
>>>>>       if (err == -ENOSPC && total_allocated_on_err) {
>>>>>           list_splice_tail(&allocated, blocks);
>>>>>           *total_allocated_on_err = total_allocated;
>>>>>       } else {
>>>>> -        drm_buddy_free_list(mm, &allocated);
>>>>> +        drm_buddy_free_list_internal(mm, &allocated);
>>>>>       }
>>>>>         return err;
>>>>> @@ -630,11 +817,11 @@ static int __alloc_contig_try_harder(struct 
>>>>> drm_buddy *mm,
>>>>>               list_splice(&blocks_lhs, blocks);
>>>>>               return 0;
>>>>>           } else if (err != -ENOSPC) {
>>>>> -            drm_buddy_free_list(mm, blocks);
>>>>> +            drm_buddy_free_list_internal(mm, blocks);
>>>>>               return err;
>>>>>           }
>>>>>           /* Free blocks for the next iteration */
>>>>> -        drm_buddy_free_list(mm, blocks);
>>>>> +        drm_buddy_free_list_internal(mm, blocks);
>>>>>       }
>>>>>         return -ENOSPC;
>>>>> @@ -690,6 +877,8 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
>>>>>       list_del(&block->link);
>>>>>       mark_free(mm, block);
>>>>>       mm->avail += drm_buddy_block_size(mm, block);
>>>>> +    if (drm_buddy_block_is_clear(block))
>>>>> +        mm->clear_avail += drm_buddy_block_size(mm, block);
>>>>>         /* Prevent recursively freeing this node */
>>>>>       parent = block->parent;
>>>>> @@ -701,6 +890,8 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
>>>>>       if (err) {
>>>>>           mark_allocated(block);
>>>>>           mm->avail -= drm_buddy_block_size(mm, block);
>>>>> +        if (drm_buddy_block_is_clear(block))
>>>>> +            mm->clear_avail -= drm_buddy_block_size(mm, block);
>>>>>           list_add(&block->link, blocks);
>>>>>       }
>>>>>   @@ -709,13 +900,28 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
>>>>>   }
>>>>>   EXPORT_SYMBOL(drm_buddy_block_trim);
>>>>>   +static struct drm_buddy_block *
>>>>> +__drm_buddy_alloc_blocks(struct drm_buddy *mm,
>>>>> +             u64 start, u64 end,
>>>>> +             unsigned int order,
>>>>> +             unsigned long flags)
>>>>> +{
>>>>> +    if (flags & DRM_BUDDY_RANGE_ALLOCATION)
>>>>> +        /* Allocate traversing within the range */
>>>>> +        return  __drm_buddy_alloc_range_bias(mm, start, end,
>>>>> +                             order, flags);
>>>>> +    else
>>>>> +        /* Allocate from freelist */
>>>>> +        return alloc_from_freelist(mm, order, flags);
>>>>> +}
>>>>> +
>>>>>   /**
>>>>>    * drm_buddy_alloc_blocks - allocate power-of-two blocks
>>>>>    *
>>>>>    * @mm: DRM buddy manager to allocate from
>>>>>    * @start: start of the allowed range for this block
>>>>>    * @end: end of the allowed range for this block
>>>>> - * @size: size of the allocation
>>>>> + * @size: size of the allocation in bytes
>>>>>    * @min_block_size: alignment of the allocation
>>>>>    * @blocks: output list head to add allocated blocks
>>>>>    * @flags: DRM_BUDDY_*_ALLOCATION flags
>>>>> @@ -761,8 +967,18 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
>>>>>           return -EINVAL;
>>>>>         /* Actual range allocation */
>>>>> -    if (start + size == end)
>>>>> -        return __drm_buddy_alloc_range(mm, start, size, NULL, 
>>>>> blocks);
>>>>> +    if (start + size == end) {
>>>>> +        err =  __drm_buddy_alloc_range(mm, start, size, NULL, 
>>>>> blocks);
>>>>> +        if (err) {
>>>>> +            order = ilog2(size) - ilog2(mm->chunk_size);
>>>>> +            if (mm->clear_avail && !__force_merge(mm, order))
>>>>> +                return __drm_buddy_alloc_range(mm, start, size, 
>>>>> NULL, blocks);
>>>>
>>>> That seems strange at a glance. With an actual range allocation like 
>>>> with intitial fb or whatever it should just find all overlapping 
>>>> pages, splitting down where needed on the edges. Not sure why 
>>>> force_merge would factor in here?
>>> In case of not merged (fragmentation due to dirty and clear pages 
>>> split), if the memory request goes into that range, we might see the 
>>> split blocks as not free and end up returning -ENOSPC.
>>> I found this problem when I tried to allocate the max_order where the 
>>> start + size == end and it ends up entering into this code block and 
>>> it returns -ENOSPC as there is no force_merge call
>>> and the requested range max order block seen as not free. And we have 
>>> an another issue though if we use force_merge to defragment a 
>>> specific ordered block, there is a less probability that
>>> we merge back the required blocks in the specific range. For now, we 
>>> can go ahead with no force merge support for actual range allocation 
>>> if we are sure that we use this only for initial fb etc.
>>
>> Ah right, now I see. But AFAICT trouble is if we say allocate 8K range 
>> at some specific offset, like above, which fails due to clear/dirty 
>> split then calling force_merge(order(8K)) is not always going to help. 
>> It will just force merge enough for 8K, but that might not be the 8K 
>> at the offset we were looking for, so it still fails. I think perhaps 
>> update the dfs in alloc_range to not bail when contains && split. Or I 
>> think other option is to force merge the entire address space on err. 
>> Other ideas?
> For actual range allocation, if the min_block_size == mm->chunk_size, I 
> think we can proceed with your first idea (i.e) update the dfs in 
> alloc_range to not bail when contains && split.
> For bias range allocation, we need to add range support to the 
> force_merge, for instance we can just continue in the force_merge for 
> loop if the contains(start, end, block_start, block_end) returns false.
> Thoughts?

Yeah, I think something like that should work.

> 
> Thanks,
> Arun.
>>
>>>
>>> If there are no major concerns, can we push these patches?
>>>
>>> Regards,
>>> Arun.
>>>>> +
>>>>> +            return err;
>>>>> +        }
>>>>> +
>>>>> +        return err;
>>>>> +    }
>>>>>         original_size = size;
>>>>>       original_min_size = min_block_size;
>>>>> @@ -786,23 +1002,34 @@ int drm_buddy_alloc_blocks(struct drm_buddy 
>>>>> *mm,
>>>>>           BUG_ON(order < min_order);
>>>>>             do {
>>>>> -            if (flags & DRM_BUDDY_RANGE_ALLOCATION)
>>>>> -                /* Allocate traversing within the range */
>>>>> -                block = alloc_range_bias(mm, start, end, order);
>>>>> -            else
>>>>> -                /* Allocate from freelist */
>>>>> -                block = alloc_from_freelist(mm, order, flags);
>>>>> -
>>>>> +            block = __drm_buddy_alloc_blocks(mm, start,
>>>>> +                             end,
>>>>> +                             order,
>>>>> +                             flags);
>>>>>               if (!IS_ERR(block))
>>>>>                   break;
>>>>>                 if (order-- == min_order) {
>>>>> +                /**
>>>>> +                 * Try allocation through force merge method
>>>>> +                 */
>>>>> +                if (mm->clear_avail && !__force_merge(mm, 
>>>>> min_order)) {
>>>>> +                    block = __drm_buddy_alloc_blocks(mm, start,
>>>>> +                                     end,
>>>>> +                                     min_order,
>>>>> +                                     flags);
>>>>> +                    if (!IS_ERR(block)) {
>>>>> +                        order = min_order;
>>>>> +                        break;
>>>>> +                    }
>>>>> +                }
>>>>> +
>>>>> +    ��           /**
>>>>> +                 * Try contiguous block allocation through
>>>>> +                 * try harder method.
>>>>> +                 */
>>>>>                   if (flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION &&
>>>>>                       !(flags & DRM_BUDDY_RANGE_ALLOCATION))
>>>>> -                    /*
>>>>> -                     * Try contiguous block allocation through
>>>>> -                     * try harder method
>>>>> -                     */
>>>>>                       return __alloc_contig_try_harder(mm,
>>>>>                                        original_size,
>>>>>                                        original_min_size,
>>>>> @@ -814,6 +1041,8 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
>>>>>             mark_allocated(block);
>>>>>           mm->avail -= drm_buddy_block_size(mm, block);
>>>>> +        if (drm_buddy_block_is_clear(block))
>>>>> +            mm->clear_avail -= drm_buddy_block_size(mm, block);
>>>>>           kmemleak_update_trace(block);
>>>>>           list_add_tail(&block->link, &allocated);
>>>>>   @@ -823,7 +1052,9 @@ int drm_buddy_alloc_blocks(struct drm_buddy 
>>>>> *mm,
>>>>>               break;
>>>>>       } while (1);
>>>>>   -    /* Trim the allocated block to the required size */
>>>>> +    /**
>>>>> +     * Trim the allocated block to the required size
>>>>> +     */
>>>>>       if (original_size != size) {
>>>>>           struct list_head *trim_list;
>>>>>           LIST_HEAD(temp);
>>>>> @@ -852,7 +1083,7 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
>>>>>       return 0;
>>>>>     err_free:
>>>>> -    drm_buddy_free_list(mm, &allocated);
>>>>> +    drm_buddy_free_list_internal(mm, &allocated);
>>>>>       return err;
>>>>>   }
>>>>>   EXPORT_SYMBOL(drm_buddy_alloc_blocks);
>>>>> @@ -885,8 +1116,8 @@ void drm_buddy_print(struct drm_buddy *mm, 
>>>>> struct drm_printer *p)
>>>>>   {
>>>>>       int order;
>>>>>   -    drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: 
>>>>> %lluMiB\n",
>>>>> -           mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20);
>>>>> +    drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: 
>>>>> %lluMiB, clear_free: %lluMiB\n",
>>>>> +           mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, 
>>>>> mm->clear_avail >> 20);
>>>>>         for (order = mm->max_order; order >= 0; order--) {
>>>>>           struct drm_buddy_block *block;
>>>>> diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c 
>>>>> b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
>>>>> index 0d735d5c2b35..942345548bc3 100644
>>>>> --- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
>>>>> +++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
>>>>> @@ -126,7 +126,7 @@ static int i915_ttm_buddy_man_alloc(struct 
>>>>> ttm_resource_manager *man,
>>>>>       return 0;
>>>>>     err_free_blocks:
>>>>> -    drm_buddy_free_list(mm, &bman_res->blocks);
>>>>> +    drm_buddy_free_list(mm, &bman_res->blocks, 0);
>>>>>       mutex_unlock(&bman->lock);
>>>>>   err_free_res:
>>>>>       ttm_resource_fini(man, &bman_res->base);
>>>>> @@ -141,7 +141,7 @@ static void i915_ttm_buddy_man_free(struct 
>>>>> ttm_resource_manager *man,
>>>>>       struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
>>>>>         mutex_lock(&bman->lock);
>>>>> -    drm_buddy_free_list(&bman->mm, &bman_res->blocks);
>>>>> +    drm_buddy_free_list(&bman->mm, &bman_res->blocks, 0);
>>>>>       bman->visible_avail += bman_res->used_visible_size;
>>>>>       mutex_unlock(&bman->lock);
>>>>>   @@ -345,7 +345,7 @@ int i915_ttm_buddy_man_fini(struct ttm_device 
>>>>> *bdev, unsigned int type)
>>>>>       ttm_set_driver_manager(bdev, type, NULL);
>>>>>         mutex_lock(&bman->lock);
>>>>> -    drm_buddy_free_list(mm, &bman->reserved);
>>>>> +    drm_buddy_free_list(mm, &bman->reserved, 0);
>>>>>       drm_buddy_fini(mm);
>>>>>       bman->visible_avail += bman->visible_reserved;
>>>>>       WARN_ON_ONCE(bman->visible_avail != bman->visible_size);
>>>>> diff --git a/drivers/gpu/drm/tests/drm_buddy_test.c 
>>>>> b/drivers/gpu/drm/tests/drm_buddy_test.c
>>>>> index 2f32fb2f12e7..454ad9952f56 100644
>>>>> --- a/drivers/gpu/drm/tests/drm_buddy_test.c
>>>>> +++ b/drivers/gpu/drm/tests/drm_buddy_test.c
>>>>> @@ -64,7 +64,7 @@ static void 
>>>>> drm_test_buddy_alloc_contiguous(struct kunit *test)
>>>>> DRM_BUDDY_CONTIGUOUS_ALLOCATION),
>>>>>                      "buddy_alloc didn't error size=%u\n", 3 * ps);
>>>>>   -    drm_buddy_free_list(&mm, &middle);
>>>>> +    drm_buddy_free_list(&mm, &middle, 0);
>>>>>       KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, 
>>>>> mm_size,
>>>>>                                  3 * ps, ps, &allocated,
>>>>> DRM_BUDDY_CONTIGUOUS_ALLOCATION),
>>>>> @@ -74,7 +74,7 @@ static void 
>>>>> drm_test_buddy_alloc_contiguous(struct kunit *test)
>>>>> DRM_BUDDY_CONTIGUOUS_ALLOCATION),
>>>>>                      "buddy_alloc didn't error size=%u\n", 2 * ps);
>>>>>   -    drm_buddy_free_list(&mm, &right);
>>>>> +    drm_buddy_free_list(&mm, &right, 0);
>>>>>       KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, 
>>>>> mm_size,
>>>>>                                  3 * ps, ps, &allocated,
>>>>> DRM_BUDDY_CONTIGUOUS_ALLOCATION),
>>>>> @@ -89,7 +89,7 @@ static void 
>>>>> drm_test_buddy_alloc_contiguous(struct kunit *test)
>>>>> DRM_BUDDY_CONTIGUOUS_ALLOCATION),
>>>>>                      "buddy_alloc hit an error size=%u\n", 2 * ps);
>>>>>   -    drm_buddy_free_list(&mm, &left);
>>>>> +    drm_buddy_free_list(&mm, &left, 0);
>>>>>       KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, 
>>>>> mm_size,
>>>>>                                   3 * ps, ps, &allocated,
>>>>> DRM_BUDDY_CONTIGUOUS_ALLOCATION),
>>>>> @@ -101,7 +101,7 @@ static void 
>>>>> drm_test_buddy_alloc_contiguous(struct kunit *test)
>>>>>         KUNIT_ASSERT_EQ(test, total, ps * 2 + ps * 3);
>>>>>   -    drm_buddy_free_list(&mm, &allocated);
>>>>> +    drm_buddy_free_list(&mm, &allocated, 0);
>>>>>       drm_buddy_fini(&mm);
>>>>>   }
>>>>>   @@ -170,7 +170,7 @@ static void 
>>>>> drm_test_buddy_alloc_pathological(struct kunit *test)
>>>>>                                 top, max_order);
>>>>>       }
>>>>>   -    drm_buddy_free_list(&mm, &holes);
>>>>> +    drm_buddy_free_list(&mm, &holes, 0);
>>>>>         /* Nothing larger than blocks of chunk_size now available */
>>>>>       for (order = 1; order <= max_order; order++) {
>>>>> @@ -182,7 +182,7 @@ static void 
>>>>> drm_test_buddy_alloc_pathological(struct kunit *test)
>>>>>       }
>>>>>         list_splice_tail(&holes, &blocks);
>>>>> -    drm_buddy_free_list(&mm, &blocks);
>>>>> +    drm_buddy_free_list(&mm, &blocks, 0);
>>>>>       drm_buddy_fini(&mm);
>>>>>   }
>>>>>   @@ -277,7 +277,7 @@ static void 
>>>>> drm_test_buddy_alloc_pessimistic(struct kunit *test)
>>>>>         list_del(&block->link);
>>>>>       drm_buddy_free_block(&mm, block);
>>>>> -    drm_buddy_free_list(&mm, &blocks);
>>>>> +    drm_buddy_free_list(&mm, &blocks, 0);
>>>>>       drm_buddy_fini(&mm);
>>>>>   }
>>>>>   @@ -323,7 +323,7 @@ static void 
>>>>> drm_test_buddy_alloc_optimistic(struct kunit *test)
>>>>>                                  size, size, &tmp, flags),
>>>>>                             "buddy_alloc unexpectedly succeeded, it 
>>>>> should be full!");
>>>>>   -    drm_buddy_free_list(&mm, &blocks);
>>>>> +    drm_buddy_free_list(&mm, &blocks, 0);
>>>>>       drm_buddy_fini(&mm);
>>>>>   }
>>>>>   @@ -358,7 +358,7 @@ static void drm_test_buddy_alloc_limit(struct 
>>>>> kunit *test)
>>>>>                           drm_buddy_block_size(&mm, block),
>>>>>                           BIT_ULL(mm.max_order) * PAGE_SIZE);
>>>>>   -    drm_buddy_free_list(&mm, &allocated);
>>>>> +    drm_buddy_free_list(&mm, &allocated, 0);
>>>>>       drm_buddy_fini(&mm);
>>>>>   }
>>>>>   diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c 
>>>>> b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
>>>>> index 115ec745e502..1ad678b62c4a 100644
>>>>> --- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
>>>>> +++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
>>>>> @@ -196,7 +196,7 @@ static int xe_ttm_vram_mgr_new(struct 
>>>>> ttm_resource_manager *man,
>>>>>       return 0;
>>>>>     error_free_blocks:
>>>>> -    drm_buddy_free_list(mm, &vres->blocks);
>>>>> +    drm_buddy_free_list(mm, &vres->blocks, 0);
>>>>>       mutex_unlock(&mgr->lock);
>>>>>   error_fini:
>>>>>       ttm_resource_fini(man, &vres->base);
>>>>> @@ -214,7 +214,7 @@ static void xe_ttm_vram_mgr_del(struct 
>>>>> ttm_resource_manager *man,
>>>>>       struct drm_buddy *mm = &mgr->mm;
>>>>>         mutex_lock(&mgr->lock);
>>>>> -    drm_buddy_free_list(mm, &vres->blocks);
>>>>> +    drm_buddy_free_list(mm, &vres->blocks, 0);
>>>>>       mgr->visible_avail += vres->used_visible_size;
>>>>>       mutex_unlock(&mgr->lock);
>>>>>   diff --git a/include/drm/drm_buddy.h b/include/drm/drm_buddy.h
>>>>> index a5b39fc01003..82570f77e817 100644
>>>>> --- a/include/drm/drm_buddy.h
>>>>> +++ b/include/drm/drm_buddy.h
>>>>> @@ -25,6 +25,8 @@
>>>>>   #define DRM_BUDDY_RANGE_ALLOCATION        BIT(0)
>>>>>   #define DRM_BUDDY_TOPDOWN_ALLOCATION        BIT(1)
>>>>>   #define DRM_BUDDY_CONTIGUOUS_ALLOCATION        BIT(2)
>>>>> +#define DRM_BUDDY_CLEAR_ALLOCATION        BIT(3)
>>>>> +#define DRM_BUDDY_CLEARED            BIT(4)
>>>>>     struct drm_buddy_block {
>>>>>   #define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
>>>>> @@ -32,8 +34,9 @@ struct drm_buddy_block {
>>>>>   #define   DRM_BUDDY_ALLOCATED       (1 << 10)
>>>>>   #define   DRM_BUDDY_FREE       (2 << 10)
>>>>>   #define   DRM_BUDDY_SPLIT       (3 << 10)
>>>>> +#define DRM_BUDDY_HEADER_CLEAR  GENMASK_ULL(9, 9)
>>>>>   /* Free to be used, if needed in the future */
>>>>> -#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(9, 6)
>>>>> +#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(8, 6)
>>>>>   #define DRM_BUDDY_HEADER_ORDER  GENMASK_ULL(5, 0)
>>>>>       u64 header;
>>>>>   @@ -86,6 +89,7 @@ struct drm_buddy {
>>>>>       u64 chunk_size;
>>>>>       u64 size;
>>>>>       u64 avail;
>>>>> +    u64 clear_avail;
>>>>>   };
>>>>>     static inline u64
>>>>> @@ -112,6 +116,12 @@ drm_buddy_block_is_allocated(struct 
>>>>> drm_buddy_block *block)
>>>>>       return drm_buddy_block_state(block) == DRM_BUDDY_ALLOCATED;
>>>>>   }
>>>>>   +static inline bool
>>>>> +drm_buddy_block_is_clear(struct drm_buddy_block *block)
>>>>> +{
>>>>> +    return block->header & DRM_BUDDY_HEADER_CLEAR;
>>>>> +}
>>>>> +
>>>>>   static inline bool
>>>>>   drm_buddy_block_is_free(struct drm_buddy_block *block)
>>>>>   {
>>>>> @@ -150,7 +160,9 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
>>>>>     void drm_buddy_free_block(struct drm_buddy *mm, struct 
>>>>> drm_buddy_block *block);
>>>>>   -void drm_buddy_free_list(struct drm_buddy *mm, struct list_head 
>>>>> *objects);
>>>>> +void drm_buddy_free_list(struct drm_buddy *mm,
>>>>> +             struct list_head *objects,
>>>>> +             unsigned int flags);
>>>>>     void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p);
>>>>>   void drm_buddy_block_print(struct drm_buddy *mm,
>>>
> 


More information about the dri-devel mailing list