[PATCH v3 1/6] drm: move the buddy allocator from i915 into common drm

Arunpravin arunpravin.paneerselvam at amd.com
Wed Nov 17 17:05:48 UTC 2021


Hi Christian,
I will make this a separate module.

Thanks,
Arun

On 17/11/21 1:33 pm, Christian König wrote:
> I've looked a bit more into this and I think we should just follow 
> Thomas Zimmermann's idea to make this a separate module.
> 
> Otherwise we just have the code around all the time even if it is unused 
> and implementing this should be trivial.
> 
> See how DRM_GEM_CMA_HELPER or DRM_VRAM_HELPER are done in 
> drivers/gpu/drm/Kconfig and then have the desired effect in 
> drivers/gpu/drm/Makefile
> 
> Should be something like ~15 lines of additional configuration which 
> saves a bit of memory on embedded systems which just need a fbdev 
> replacement.
> 
> Thanks,
> Christian.
> 
> Am 16.11.21 um 21:18 schrieb Arunpravin:
>> Move the base i915 buddy allocator code into drm
>> - Move i915_buddy.h to include/drm
>> - Move i915_buddy.c to drm root folder
>> - Rename "i915" string with "drm" string wherever applicable
>> - Rename "I915" string with "DRM" string wherever applicable
>> - Fix header file dependencies
>> - Fix alignment issues
>> - add Makefile support for drm buddy
>> - export functions and write kerneldoc description
>> - Remove i915 selftest config check condition as buddy selftest
>>    will be moved to drm selftest folder
>>
>> cleanup i915 buddy references in i915 driver module
>> and replace with drm buddy
>>
>> v2:
>>    - include header file in alphabetical order (Thomas)
>>    - merged changes listed in the body section into a single patch
>>      to keep the build intact (Christian, Jani)
>>
>> Signed-off-by: Arunpravin <Arunpravin.PaneerSelvam at amd.com>
>> ---
>>   drivers/gpu/drm/Makefile                      |   2 +-
>>   drivers/gpu/drm/drm_buddy.c                   | 521 ++++++++++++++++++
>>   drivers/gpu/drm/drm_drv.c                     |   3 +
>>   drivers/gpu/drm/i915/Makefile                 |   1 -
>>   drivers/gpu/drm/i915/i915_buddy.c             | 466 ----------------
>>   drivers/gpu/drm/i915/i915_buddy.h             | 143 -----
>>   drivers/gpu/drm/i915/i915_module.c            |   3 -
>>   drivers/gpu/drm/i915/i915_scatterlist.c       |  11 +-
>>   drivers/gpu/drm/i915/i915_ttm_buddy_manager.c |  33 +-
>>   drivers/gpu/drm/i915/i915_ttm_buddy_manager.h |   4 +-
>>   include/drm/drm_buddy.h                       | 153 +++++
>>   11 files changed, 702 insertions(+), 638 deletions(-)
>>   create mode 100644 drivers/gpu/drm/drm_buddy.c
>>   delete mode 100644 drivers/gpu/drm/i915/i915_buddy.c
>>   delete mode 100644 drivers/gpu/drm/i915/i915_buddy.h
>>   create mode 100644 include/drm/drm_buddy.h
>>
>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>> index 0dff40bb863c..dc61e91a3154 100644
>> --- a/drivers/gpu/drm/Makefile
>> +++ b/drivers/gpu/drm/Makefile
>> @@ -18,7 +18,7 @@ drm-y       :=	drm_aperture.o drm_auth.o drm_cache.o \
>>   		drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \
>>   		drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \
>>   		drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \
>> -		drm_managed.o drm_vblank_work.o
>> +		drm_managed.o drm_vblank_work.o drm_buddy.o
>>   
>>   drm-$(CONFIG_DRM_LEGACY) += drm_agpsupport.o drm_bufs.o drm_context.o drm_dma.o \
>>   			    drm_legacy_misc.o drm_lock.o drm_memory.o drm_scatter.o \
>> diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c
>> new file mode 100644
>> index 000000000000..39eb1d224bec
>> --- /dev/null
>> +++ b/drivers/gpu/drm/drm_buddy.c
>> @@ -0,0 +1,521 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2021 Intel Corporation
>> + */
>> +
>> +#include <linux/kmemleak.h>
>> +#include <linux/sizes.h>
>> +
>> +#include <drm/drm_buddy.h>
>> +
>> +static struct kmem_cache *slab_blocks;
>> +
>> +static struct drm_buddy_block *drm_block_alloc(struct drm_buddy_mm *mm,
>> +					       struct drm_buddy_block *parent,
>> +					       unsigned int order,
>> +					       u64 offset)
>> +{
>> +	struct drm_buddy_block *block;
>> +
>> +	BUG_ON(order > DRM_BUDDY_MAX_ORDER);
>> +
>> +	block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL);
>> +	if (!block)
>> +		return NULL;
>> +
>> +	block->header = offset;
>> +	block->header |= order;
>> +	block->parent = parent;
>> +
>> +	BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED);
>> +	return block;
>> +}
>> +
>> +static void drm_block_free(struct drm_buddy_mm *mm,
>> +			   struct drm_buddy_block *block)
>> +{
>> +	kmem_cache_free(slab_blocks, block);
>> +}
>> +
>> +static void mark_allocated(struct drm_buddy_block *block)
>> +{
>> +	block->header &= ~DRM_BUDDY_HEADER_STATE;
>> +	block->header |= DRM_BUDDY_ALLOCATED;
>> +
>> +	list_del(&block->link);
>> +}
>> +
>> +static void mark_free(struct drm_buddy_mm *mm,
>> +		      struct drm_buddy_block *block)
>> +{
>> +	block->header &= ~DRM_BUDDY_HEADER_STATE;
>> +	block->header |= DRM_BUDDY_FREE;
>> +
>> +	list_add(&block->link,
>> +		 &mm->free_list[drm_buddy_block_order(block)]);
>> +}
>> +
>> +static void mark_split(struct drm_buddy_block *block)
>> +{
>> +	block->header &= ~DRM_BUDDY_HEADER_STATE;
>> +	block->header |= DRM_BUDDY_SPLIT;
>> +
>> +	list_del(&block->link);
>> +}
>> +
>> +/**
>> + * drm_buddy_init - init memory manager
>> + *
>> + * @mm: DRM buddy manager to initialize
>> + * @size: size in bytes to manage
>> + * @chunk_size: minimum page size in bytes for our allocations
>> + *
>> + * Initializes the memory manager and its resources.
>> + *
>> + * Returns:
>> + * 0 on success, error code on failure.
>> + */
>> +int drm_buddy_init(struct drm_buddy_mm *mm, u64 size, u64 chunk_size)
>> +{
>> +	unsigned int i;
>> +	u64 offset;
>> +
>> +	if (size < chunk_size)
>> +		return -EINVAL;
>> +
>> +	if (chunk_size < PAGE_SIZE)
>> +		return -EINVAL;
>> +
>> +	if (!is_power_of_2(chunk_size))
>> +		return -EINVAL;
>> +
>> +	size = round_down(size, chunk_size);
>> +
>> +	mm->size = size;
>> +	mm->avail = size;
>> +	mm->chunk_size = chunk_size;
>> +	mm->max_order = ilog2(size) - ilog2(chunk_size);
>> +
>> +	BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER);
>> +
>> +	mm->free_list = kmalloc_array(mm->max_order + 1,
>> +				      sizeof(struct list_head),
>> +				      GFP_KERNEL);
>> +	if (!mm->free_list)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i <= mm->max_order; ++i)
>> +		INIT_LIST_HEAD(&mm->free_list[i]);
>> +
>> +	mm->n_roots = hweight64(size);
>> +
>> +	mm->roots = kmalloc_array(mm->n_roots,
>> +				  sizeof(struct drm_buddy_block *),
>> +				  GFP_KERNEL);
>> +	if (!mm->roots)
>> +		goto out_free_list;
>> +
>> +	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.
>> +	 */
>> +	do {
>> +		struct drm_buddy_block *root;
>> +		unsigned int order;
>> +		u64 root_size;
>> +
>> +		root_size = rounddown_pow_of_two(size);
>> +		order = ilog2(root_size) - ilog2(chunk_size);
>> +
>> +		root = drm_block_alloc(mm, NULL, order, offset);
>> +		if (!root)
>> +			goto out_free_roots;
>> +
>> +		mark_free(mm, root);
>> +
>> +		BUG_ON(i > mm->max_order);
>> +		BUG_ON(drm_buddy_block_size(mm, root) < chunk_size);
>> +
>> +		mm->roots[i] = root;
>> +
>> +		offset += root_size;
>> +		size -= root_size;
>> +		i++;
>> +	} while (size);
>> +
>> +	return 0;
>> +
>> +out_free_roots:
>> +	while (i--)
>> +		drm_block_free(mm, mm->roots[i]);
>> +	kfree(mm->roots);
>> +out_free_list:
>> +	kfree(mm->free_list);
>> +	return -ENOMEM;
>> +}
>> +EXPORT_SYMBOL(drm_buddy_init);
>> +
>> +/**
>> + * drm_buddy_fini - tear down the memory manager
>> + *
>> + * @mm: DRM buddy manager to free
>> + *
>> + * Cleanup memory manager resources and the freelist
>> + */
>> +void drm_buddy_fini(struct drm_buddy_mm *mm)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < mm->n_roots; ++i) {
>> +		WARN_ON(!drm_buddy_block_is_free(mm->roots[i]));
>> +		drm_block_free(mm, mm->roots[i]);
>> +	}
>> +
>> +	WARN_ON(mm->avail != mm->size);
>> +
>> +	kfree(mm->roots);
>> +	kfree(mm->free_list);
>> +}
>> +EXPORT_SYMBOL(drm_buddy_fini);
>> +
>> +static int split_block(struct drm_buddy_mm *mm,
>> +		       struct drm_buddy_block *block)
>> +{
>> +	unsigned int block_order = drm_buddy_block_order(block) - 1;
>> +	u64 offset = drm_buddy_block_offset(block);
>> +
>> +	BUG_ON(!drm_buddy_block_is_free(block));
>> +	BUG_ON(!drm_buddy_block_order(block));
>> +
>> +	block->left = drm_block_alloc(mm, block, block_order, offset);
>> +	if (!block->left)
>> +		return -ENOMEM;
>> +
>> +	block->right = drm_block_alloc(mm, block, block_order,
>> +					offset + (mm->chunk_size << block_order));
>> +	if (!block->right) {
>> +		drm_block_free(mm, block->left);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	mark_free(mm, block->left);
>> +	mark_free(mm, block->right);
>> +
>> +	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;
>> +}
>> +
>> +static void __drm_buddy_free(struct drm_buddy_mm *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);
>> +}
>> +
>> +void drm_buddy_free(struct drm_buddy_mm *mm,
>> +		    struct drm_buddy_block *block)
>> +{
>> +	BUG_ON(!drm_buddy_block_is_allocated(block));
>> +	mm->avail += drm_buddy_block_size(mm, block);
>> +	__drm_buddy_free(mm, 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 *mm, struct list_head *objects)
>> +{
>> +	struct drm_buddy_block *block, *on;
>> +
>> +	list_for_each_entry_safe(block, on, objects, link) {
>> +		drm_buddy_free(mm, block);
>> +		cond_resched();
>> +	}
>> +	INIT_LIST_HEAD(objects);
>> +}
>> +EXPORT_SYMBOL(drm_buddy_free_list);
>> +
>> +/**
>> + * drm_buddy_alloc - allocate power-of-two blocks
>> + *
>> + * @mm: DRM buddy manager to allocate from
>> + * @order: size of the allocation
>> + *
>> + * The order value here translates to:
>> + *
>> + * 0 = 2^0 * mm->chunk_size
>> + * 1 = 2^1 * mm->chunk_size
>> + * 2 = 2^2 * mm->chunk_size
>> + *
>> + * Returns:
>> + * allocated ptr to the &drm_buddy_block on success
>> + */
>> +struct drm_buddy_block *
>> +drm_buddy_alloc(struct drm_buddy_mm *mm, unsigned int order)
>> +{
>> +	struct drm_buddy_block *block = NULL;
>> +	unsigned int i;
>> +	int err;
>> +
>> +	for (i = order; i <= mm->max_order; ++i) {
>> +		block = list_first_entry_or_null(&mm->free_list[i],
>> +						 struct drm_buddy_block,
>> +						 link);
>> +		if (block)
>> +			break;
>> +	}
>> +
>> +	if (!block)
>> +		return ERR_PTR(-ENOSPC);
>> +
>> +	BUG_ON(!drm_buddy_block_is_free(block));
>> +
>> +	while (i != order) {
>> +		err = split_block(mm, block);
>> +		if (unlikely(err))
>> +			goto out_free;
>> +
>> +		/* Go low */
>> +		block = block->left;
>> +		i--;
>> +	}
>> +
>> +	mark_allocated(block);
>> +	mm->avail -= drm_buddy_block_size(mm, block);
>> +	kmemleak_update_trace(block);
>> +	return block;
>> +
>> +out_free:
>> +	if (i != order)
>> +		__drm_buddy_free(mm, block);
>> +	return ERR_PTR(err);
>> +}
>> +EXPORT_SYMBOL(drm_buddy_alloc);
>> +
>> +static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
>> +{
>> +	return s1 <= e2 && e1 >= s2;
>> +}
>> +
>> +static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
>> +{
>> +	return s1 <= s2 && e1 >= e2;
>> +}
>> +
>> +/**
>> + * drm_buddy_alloc_range - allocate range
>> + *
>> + * @mm: DRM buddy manager to allocate from
>> + * @blocks: output list head to add allocated blocks
>> + * @start: start of the allowed range for this block
>> + * @size: size of the allocation
>> + *
>> + * Intended for pre-allocating portions of the address space, for example to
>> + * reserve a block for the initial framebuffer or similar, hence the expectation
>> + * here is that drm_buddy_alloc() is still the main vehicle for
>> + * allocations, so if that's not the case then the drm_mm range allocator is
>> + * probably a much better fit, and so you should probably go use that instead.
>> + *
>> + * Note that it's safe to chain together multiple alloc_ranges
>> + * with the same blocks list
>> + *
>> + * Returns:
>> + * 0 on success, error code on failure.
>> + */
>> +int drm_buddy_alloc_range(struct drm_buddy_mm *mm,
>> +			  struct list_head *blocks,
>> +			  u64 start, u64 size)
>> +{
>> +	struct drm_buddy_block *block;
>> +	struct drm_buddy_block *buddy;
>> +	LIST_HEAD(allocated);
>> +	LIST_HEAD(dfs);
>> +	u64 end;
>> +	int err;
>> +	int i;
>> +
>> +	if (size < mm->chunk_size)
>> +		return -EINVAL;
>> +
>> +	if (!IS_ALIGNED(size | start, mm->chunk_size))
>> +		return -EINVAL;
>> +
>> +	if (range_overflows(start, size, mm->size))
>> +		return -EINVAL;
>> +
>> +	for (i = 0; i < mm->n_roots; ++i)
>> +		list_add_tail(&mm->roots[i]->tmp_link, &dfs);
>> +
>> +	end = start + size - 1;
>> +
>> +	do {
>> +		u64 block_start;
>> +		u64 block_end;
>> +
>> +		block = list_first_entry_or_null(&dfs,
>> +						 struct drm_buddy_block,
>> +						 tmp_link);
>> +		if (!block)
>> +			break;
>> +
>> +		list_del(&block->tmp_link);
>> +
>> +		block_start = drm_buddy_block_offset(block);
>> +		block_end = block_start + drm_buddy_block_size(mm, block) - 1;
>> +
>> +		if (!overlaps(start, end, block_start, block_end))
>> +			continue;
>> +
>> +		if (drm_buddy_block_is_allocated(block)) {
>> +			err = -ENOSPC;
>> +			goto err_free;
>> +		}
>> +
>> +		if (contains(start, end, block_start, block_end)) {
>> +			if (!drm_buddy_block_is_free(block)) {
>> +				err = -ENOSPC;
>> +				goto err_free;
>> +			}
>> +
>> +			mark_allocated(block);
>> +			mm->avail -= drm_buddy_block_size(mm, block);
>> +			list_add_tail(&block->link, &allocated);
>> +			continue;
>> +		}
>> +
>> +		if (!drm_buddy_block_is_split(block)) {
>> +			err = split_block(mm, block);
>> +			if (unlikely(err))
>> +				goto err_undo;
>> +		}
>> +
>> +		list_add(&block->right->tmp_link, &dfs);
>> +		list_add(&block->left->tmp_link, &dfs);
>> +	} while (1);
>> +
>> +	list_splice_tail(&allocated, blocks);
>> +	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.
>> +	 */
>> +	buddy = get_buddy(block);
>> +	if (buddy &&
>> +	    (drm_buddy_block_is_free(block) &&
>> +	     drm_buddy_block_is_free(buddy)))
>> +		__drm_buddy_free(mm, block);
>> +
>> +err_free:
>> +	drm_buddy_free_list(mm, &allocated);
>> +	return err;
>> +}
>> +EXPORT_SYMBOL(drm_buddy_alloc_range);
>> +
>> +/**
>> + * drm_buddy_block_print - print block information
>> + *
>> + * @mm: DRM buddy manager
>> + * @block: DRM buddy block
>> + * @p: DRM printer to use
>> + */
>> +void drm_buddy_block_print(struct drm_buddy_mm *mm,
>> +			   struct drm_buddy_block *block,
>> +			   struct drm_printer *p)
>> +{
>> +	u64 start = drm_buddy_block_offset(block);
>> +	u64 size = drm_buddy_block_size(mm, block);
>> +
>> +	drm_printf(p, "%#018llx-%#018llx: %llu\n", start, start + size, size);
>> +}
>> +EXPORT_SYMBOL(drm_buddy_block_print);
>> +
>> +/**
>> + * drm_buddy_print - print allocator state
>> + *
>> + * @mm: DRM buddy manager
>> + * @p: DRM printer to use
>> + */
>> +void drm_buddy_print(struct drm_buddy_mm *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);
>> +
>> +	for (order = mm->max_order; order >= 0; order--) {
>> +		struct drm_buddy_block *block;
>> +		u64 count = 0, free;
>> +
>> +		list_for_each_entry(block, &mm->free_list[order], link) {
>> +			BUG_ON(!drm_buddy_block_is_free(block));
>> +			count++;
>> +		}
>> +
>> +		drm_printf(p, "order-%d ", order);
>> +
>> +		free = count * (mm->chunk_size << order);
>> +		if (free < SZ_1M)
>> +			drm_printf(p, "free: %lluKiB", free >> 10);
>> +		else
>> +			drm_printf(p, "free: %lluMiB", free >> 20);
>> +
>> +		drm_printf(p, ", pages: %llu\n", count);
>> +	}
>> +}
>> +EXPORT_SYMBOL(drm_buddy_print);
>> +
>> +void drm_buddy_module_exit(void)
>> +{
>> +	kmem_cache_destroy(slab_blocks);
>> +}
>> +
>> +int __init drm_buddy_module_init(void)
>> +{
>> +	slab_blocks = KMEM_CACHE(drm_buddy_block, 0);
>> +	if (!slab_blocks)
>> +		return -ENOMEM;
>> +
>> +	return 0;
>> +}
>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>> index 7a5097467ba5..5d5e46e60586 100644
>> --- a/drivers/gpu/drm/drm_drv.c
>> +++ b/drivers/gpu/drm/drm_drv.c
>> @@ -35,6 +35,7 @@
>>   #include <linux/slab.h>
>>   #include <linux/srcu.h>
>>   
>> +#include <drm/drm_buddy.h>
>>   #include <drm/drm_cache.h>
>>   #include <drm/drm_client.h>
>>   #include <drm/drm_color_mgmt.h>
>> @@ -1034,6 +1035,7 @@ static void drm_core_exit(void)
>>   	drm_sysfs_destroy();
>>   	idr_destroy(&drm_minors_idr);
>>   	drm_connector_ida_destroy();
>> +	drm_buddy_module_exit();
>>   }
>>   
>>   static int __init drm_core_init(void)
>> @@ -1043,6 +1045,7 @@ static int __init drm_core_init(void)
>>   	drm_connector_ida_init();
>>   	idr_init(&drm_minors_idr);
>>   	drm_memcpy_init_early();
>> +	drm_buddy_module_init();
>>   
>>   	ret = drm_sysfs_init();
>>   	if (ret < 0) {
>> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>> index 467872cca027..fc5ca8c4ccb2 100644
>> --- a/drivers/gpu/drm/i915/Makefile
>> +++ b/drivers/gpu/drm/i915/Makefile
>> @@ -161,7 +161,6 @@ gem-y += \
>>   i915-y += \
>>   	  $(gem-y) \
>>   	  i915_active.o \
>> -	  i915_buddy.o \
>>   	  i915_cmd_parser.o \
>>   	  i915_gem_evict.o \
>>   	  i915_gem_gtt.o \
>> diff --git a/drivers/gpu/drm/i915/i915_buddy.c b/drivers/gpu/drm/i915/i915_buddy.c
>> deleted file mode 100644
>> index 6e2ad68f8f3f..000000000000
>> --- a/drivers/gpu/drm/i915/i915_buddy.c
>> +++ /dev/null
>> @@ -1,466 +0,0 @@
>> -// SPDX-License-Identifier: MIT
>> -/*
>> - * Copyright © 2021 Intel Corporation
>> - */
>> -
>> -#include <linux/kmemleak.h>
>> -#include <linux/sizes.h>
>> -
>> -#include "i915_buddy.h"
>> -
>> -#include "i915_gem.h"
>> -#include "i915_utils.h"
>> -
>> -static struct kmem_cache *slab_blocks;
>> -
>> -static struct i915_buddy_block *i915_block_alloc(struct i915_buddy_mm *mm,
>> -						 struct i915_buddy_block *parent,
>> -						 unsigned int order,
>> -						 u64 offset)
>> -{
>> -	struct i915_buddy_block *block;
>> -
>> -	GEM_BUG_ON(order > I915_BUDDY_MAX_ORDER);
>> -
>> -	block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL);
>> -	if (!block)
>> -		return NULL;
>> -
>> -	block->header = offset;
>> -	block->header |= order;
>> -	block->parent = parent;
>> -
>> -	GEM_BUG_ON(block->header & I915_BUDDY_HEADER_UNUSED);
>> -	return block;
>> -}
>> -
>> -static void i915_block_free(struct i915_buddy_mm *mm,
>> -			    struct i915_buddy_block *block)
>> -{
>> -	kmem_cache_free(slab_blocks, block);
>> -}
>> -
>> -static void mark_allocated(struct i915_buddy_block *block)
>> -{
>> -	block->header &= ~I915_BUDDY_HEADER_STATE;
>> -	block->header |= I915_BUDDY_ALLOCATED;
>> -
>> -	list_del(&block->link);
>> -}
>> -
>> -static void mark_free(struct i915_buddy_mm *mm,
>> -		      struct i915_buddy_block *block)
>> -{
>> -	block->header &= ~I915_BUDDY_HEADER_STATE;
>> -	block->header |= I915_BUDDY_FREE;
>> -
>> -	list_add(&block->link,
>> -		 &mm->free_list[i915_buddy_block_order(block)]);
>> -}
>> -
>> -static void mark_split(struct i915_buddy_block *block)
>> -{
>> -	block->header &= ~I915_BUDDY_HEADER_STATE;
>> -	block->header |= I915_BUDDY_SPLIT;
>> -
>> -	list_del(&block->link);
>> -}
>> -
>> -int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size)
>> -{
>> -	unsigned int i;
>> -	u64 offset;
>> -
>> -	if (size < chunk_size)
>> -		return -EINVAL;
>> -
>> -	if (chunk_size < PAGE_SIZE)
>> -		return -EINVAL;
>> -
>> -	if (!is_power_of_2(chunk_size))
>> -		return -EINVAL;
>> -
>> -	size = round_down(size, chunk_size);
>> -
>> -	mm->size = size;
>> -	mm->avail = size;
>> -	mm->chunk_size = chunk_size;
>> -	mm->max_order = ilog2(size) - ilog2(chunk_size);
>> -
>> -	GEM_BUG_ON(mm->max_order > I915_BUDDY_MAX_ORDER);
>> -
>> -	mm->free_list = kmalloc_array(mm->max_order + 1,
>> -				      sizeof(struct list_head),
>> -				      GFP_KERNEL);
>> -	if (!mm->free_list)
>> -		return -ENOMEM;
>> -
>> -	for (i = 0; i <= mm->max_order; ++i)
>> -		INIT_LIST_HEAD(&mm->free_list[i]);
>> -
>> -	mm->n_roots = hweight64(size);
>> -
>> -	mm->roots = kmalloc_array(mm->n_roots,
>> -				  sizeof(struct i915_buddy_block *),
>> -				  GFP_KERNEL);
>> -	if (!mm->roots)
>> -		goto out_free_list;
>> -
>> -	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.
>> -	 */
>> -	do {
>> -		struct i915_buddy_block *root;
>> -		unsigned int order;
>> -		u64 root_size;
>> -
>> -		root_size = rounddown_pow_of_two(size);
>> -		order = ilog2(root_size) - ilog2(chunk_size);
>> -
>> -		root = i915_block_alloc(mm, NULL, order, offset);
>> -		if (!root)
>> -			goto out_free_roots;
>> -
>> -		mark_free(mm, root);
>> -
>> -		GEM_BUG_ON(i > mm->max_order);
>> -		GEM_BUG_ON(i915_buddy_block_size(mm, root) < chunk_size);
>> -
>> -		mm->roots[i] = root;
>> -
>> -		offset += root_size;
>> -		size -= root_size;
>> -		i++;
>> -	} while (size);
>> -
>> -	return 0;
>> -
>> -out_free_roots:
>> -	while (i--)
>> -		i915_block_free(mm, mm->roots[i]);
>> -	kfree(mm->roots);
>> -out_free_list:
>> -	kfree(mm->free_list);
>> -	return -ENOMEM;
>> -}
>> -
>> -void i915_buddy_fini(struct i915_buddy_mm *mm)
>> -{
>> -	int i;
>> -
>> -	for (i = 0; i < mm->n_roots; ++i) {
>> -		GEM_WARN_ON(!i915_buddy_block_is_free(mm->roots[i]));
>> -		i915_block_free(mm, mm->roots[i]);
>> -	}
>> -
>> -	GEM_WARN_ON(mm->avail != mm->size);
>> -
>> -	kfree(mm->roots);
>> -	kfree(mm->free_list);
>> -}
>> -
>> -static int split_block(struct i915_buddy_mm *mm,
>> -		       struct i915_buddy_block *block)
>> -{
>> -	unsigned int block_order = i915_buddy_block_order(block) - 1;
>> -	u64 offset = i915_buddy_block_offset(block);
>> -
>> -	GEM_BUG_ON(!i915_buddy_block_is_free(block));
>> -	GEM_BUG_ON(!i915_buddy_block_order(block));
>> -
>> -	block->left = i915_block_alloc(mm, block, block_order, offset);
>> -	if (!block->left)
>> -		return -ENOMEM;
>> -
>> -	block->right = i915_block_alloc(mm, block, block_order,
>> -					offset + (mm->chunk_size << block_order));
>> -	if (!block->right) {
>> -		i915_block_free(mm, block->left);
>> -		return -ENOMEM;
>> -	}
>> -
>> -	mark_free(mm, block->left);
>> -	mark_free(mm, block->right);
>> -
>> -	mark_split(block);
>> -
>> -	return 0;
>> -}
>> -
>> -static struct i915_buddy_block *
>> -get_buddy(struct i915_buddy_block *block)
>> -{
>> -	struct i915_buddy_block *parent;
>> -
>> -	parent = block->parent;
>> -	if (!parent)
>> -		return NULL;
>> -
>> -	if (parent->left == block)
>> -		return parent->right;
>> -
>> -	return parent->left;
>> -}
>> -
>> -static void __i915_buddy_free(struct i915_buddy_mm *mm,
>> -			      struct i915_buddy_block *block)
>> -{
>> -	struct i915_buddy_block *parent;
>> -
>> -	while ((parent = block->parent)) {
>> -		struct i915_buddy_block *buddy;
>> -
>> -		buddy = get_buddy(block);
>> -
>> -		if (!i915_buddy_block_is_free(buddy))
>> -			break;
>> -
>> -		list_del(&buddy->link);
>> -
>> -		i915_block_free(mm, block);
>> -		i915_block_free(mm, buddy);
>> -
>> -		block = parent;
>> -	}
>> -
>> -	mark_free(mm, block);
>> -}
>> -
>> -void i915_buddy_free(struct i915_buddy_mm *mm,
>> -		     struct i915_buddy_block *block)
>> -{
>> -	GEM_BUG_ON(!i915_buddy_block_is_allocated(block));
>> -	mm->avail += i915_buddy_block_size(mm, block);
>> -	__i915_buddy_free(mm, block);
>> -}
>> -
>> -void i915_buddy_free_list(struct i915_buddy_mm *mm, struct list_head *objects)
>> -{
>> -	struct i915_buddy_block *block, *on;
>> -
>> -	list_for_each_entry_safe(block, on, objects, link) {
>> -		i915_buddy_free(mm, block);
>> -		cond_resched();
>> -	}
>> -	INIT_LIST_HEAD(objects);
>> -}
>> -
>> -/*
>> - * Allocate power-of-two block. The order value here translates to:
>> - *
>> - *   0 = 2^0 * mm->chunk_size
>> - *   1 = 2^1 * mm->chunk_size
>> - *   2 = 2^2 * mm->chunk_size
>> - *   ...
>> - */
>> -struct i915_buddy_block *
>> -i915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order)
>> -{
>> -	struct i915_buddy_block *block = NULL;
>> -	unsigned int i;
>> -	int err;
>> -
>> -	for (i = order; i <= mm->max_order; ++i) {
>> -		block = list_first_entry_or_null(&mm->free_list[i],
>> -						 struct i915_buddy_block,
>> -						 link);
>> -		if (block)
>> -			break;
>> -	}
>> -
>> -	if (!block)
>> -		return ERR_PTR(-ENOSPC);
>> -
>> -	GEM_BUG_ON(!i915_buddy_block_is_free(block));
>> -
>> -	while (i != order) {
>> -		err = split_block(mm, block);
>> -		if (unlikely(err))
>> -			goto out_free;
>> -
>> -		/* Go low */
>> -		block = block->left;
>> -		i--;
>> -	}
>> -
>> -	mark_allocated(block);
>> -	mm->avail -= i915_buddy_block_size(mm, block);
>> -	kmemleak_update_trace(block);
>> -	return block;
>> -
>> -out_free:
>> -	if (i != order)
>> -		__i915_buddy_free(mm, block);
>> -	return ERR_PTR(err);
>> -}
>> -
>> -static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
>> -{
>> -	return s1 <= e2 && e1 >= s2;
>> -}
>> -
>> -static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
>> -{
>> -	return s1 <= s2 && e1 >= e2;
>> -}
>> -
>> -/*
>> - * Allocate range. Note that it's safe to chain together multiple alloc_ranges
>> - * with the same blocks list.
>> - *
>> - * Intended for pre-allocating portions of the address space, for example to
>> - * reserve a block for the initial framebuffer or similar, hence the expectation
>> - * here is that i915_buddy_alloc() is still the main vehicle for
>> - * allocations, so if that's not the case then the drm_mm range allocator is
>> - * probably a much better fit, and so you should probably go use that instead.
>> - */
>> -int i915_buddy_alloc_range(struct i915_buddy_mm *mm,
>> -			   struct list_head *blocks,
>> -			   u64 start, u64 size)
>> -{
>> -	struct i915_buddy_block *block;
>> -	struct i915_buddy_block *buddy;
>> -	LIST_HEAD(allocated);
>> -	LIST_HEAD(dfs);
>> -	u64 end;
>> -	int err;
>> -	int i;
>> -
>> -	if (size < mm->chunk_size)
>> -		return -EINVAL;
>> -
>> -	if (!IS_ALIGNED(size | start, mm->chunk_size))
>> -		return -EINVAL;
>> -
>> -	if (range_overflows(start, size, mm->size))
>> -		return -EINVAL;
>> -
>> -	for (i = 0; i < mm->n_roots; ++i)
>> -		list_add_tail(&mm->roots[i]->tmp_link, &dfs);
>> -
>> -	end = start + size - 1;
>> -
>> -	do {
>> -		u64 block_start;
>> -		u64 block_end;
>> -
>> -		block = list_first_entry_or_null(&dfs,
>> -						 struct i915_buddy_block,
>> -						 tmp_link);
>> -		if (!block)
>> -			break;
>> -
>> -		list_del(&block->tmp_link);
>> -
>> -		block_start = i915_buddy_block_offset(block);
>> -		block_end = block_start + i915_buddy_block_size(mm, block) - 1;
>> -
>> -		if (!overlaps(start, end, block_start, block_end))
>> -			continue;
>> -
>> -		if (i915_buddy_block_is_allocated(block)) {
>> -			err = -ENOSPC;
>> -			goto err_free;
>> -		}
>> -
>> -		if (contains(start, end, block_start, block_end)) {
>> -			if (!i915_buddy_block_is_free(block)) {
>> -				err = -ENOSPC;
>> -				goto err_free;
>> -			}
>> -
>> -			mark_allocated(block);
>> -			mm->avail -= i915_buddy_block_size(mm, block);
>> -			list_add_tail(&block->link, &allocated);
>> -			continue;
>> -		}
>> -
>> -		if (!i915_buddy_block_is_split(block)) {
>> -			err = split_block(mm, block);
>> -			if (unlikely(err))
>> -				goto err_undo;
>> -		}
>> -
>> -		list_add(&block->right->tmp_link, &dfs);
>> -		list_add(&block->left->tmp_link, &dfs);
>> -	} while (1);
>> -
>> -	list_splice_tail(&allocated, blocks);
>> -	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.
>> -	 */
>> -	buddy = get_buddy(block);
>> -	if (buddy &&
>> -	    (i915_buddy_block_is_free(block) &&
>> -	     i915_buddy_block_is_free(buddy)))
>> -		__i915_buddy_free(mm, block);
>> -
>> -err_free:
>> -	i915_buddy_free_list(mm, &allocated);
>> -	return err;
>> -}
>> -
>> -void i915_buddy_block_print(struct i915_buddy_mm *mm,
>> -			    struct i915_buddy_block *block,
>> -			    struct drm_printer *p)
>> -{
>> -	u64 start = i915_buddy_block_offset(block);
>> -	u64 size = i915_buddy_block_size(mm, block);
>> -
>> -	drm_printf(p, "%#018llx-%#018llx: %llu\n", start, start + size, size);
>> -}
>> -
>> -void i915_buddy_print(struct i915_buddy_mm *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);
>> -
>> -	for (order = mm->max_order; order >= 0; order--) {
>> -		struct i915_buddy_block *block;
>> -		u64 count = 0, free;
>> -
>> -		list_for_each_entry(block, &mm->free_list[order], link) {
>> -			GEM_BUG_ON(!i915_buddy_block_is_free(block));
>> -			count++;
>> -		}
>> -
>> -		drm_printf(p, "order-%d ", order);
>> -
>> -		free = count * (mm->chunk_size << order);
>> -		if (free < SZ_1M)
>> -			drm_printf(p, "free: %lluKiB", free >> 10);
>> -		else
>> -			drm_printf(p, "free: %lluMiB", free >> 20);
>> -
>> -		drm_printf(p, ", pages: %llu\n", count);
>> -	}
>> -}
>> -
>> -#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
>> -#include "selftests/i915_buddy.c"
>> -#endif
>> -
>> -void i915_buddy_module_exit(void)
>> -{
>> -	kmem_cache_destroy(slab_blocks);
>> -}
>> -
>> -int __init i915_buddy_module_init(void)
>> -{
>> -	slab_blocks = KMEM_CACHE(i915_buddy_block, 0);
>> -	if (!slab_blocks)
>> -		return -ENOMEM;
>> -
>> -	return 0;
>> -}
>> diff --git a/drivers/gpu/drm/i915/i915_buddy.h b/drivers/gpu/drm/i915/i915_buddy.h
>> deleted file mode 100644
>> index 7077742112ac..000000000000
>> --- a/drivers/gpu/drm/i915/i915_buddy.h
>> +++ /dev/null
>> @@ -1,143 +0,0 @@
>> -/* SPDX-License-Identifier: MIT */
>> -/*
>> - * Copyright © 2021 Intel Corporation
>> - */
>> -
>> -#ifndef __I915_BUDDY_H__
>> -#define __I915_BUDDY_H__
>> -
>> -#include <linux/bitops.h>
>> -#include <linux/list.h>
>> -#include <linux/slab.h>
>> -
>> -#include <drm/drm_print.h>
>> -
>> -struct i915_buddy_block {
>> -#define I915_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
>> -#define I915_BUDDY_HEADER_STATE  GENMASK_ULL(11, 10)
>> -#define   I915_BUDDY_ALLOCATED	   (1 << 10)
>> -#define   I915_BUDDY_FREE	   (2 << 10)
>> -#define   I915_BUDDY_SPLIT	   (3 << 10)
>> -/* Free to be used, if needed in the future */
>> -#define I915_BUDDY_HEADER_UNUSED GENMASK_ULL(9, 6)
>> -#define I915_BUDDY_HEADER_ORDER  GENMASK_ULL(5, 0)
>> -	u64 header;
>> -
>> -	struct i915_buddy_block *left;
>> -	struct i915_buddy_block *right;
>> -	struct i915_buddy_block *parent;
>> -
>> -	void *private; /* owned by creator */
>> -
>> -	/*
>> -	 * While the block is allocated by the user through i915_buddy_alloc*,
>> -	 * the user has ownership of the link, for example to maintain within
>> -	 * a list, if so desired. As soon as the block is freed with
>> -	 * i915_buddy_free* ownership is given back to the mm.
>> -	 */
>> -	struct list_head link;
>> -	struct list_head tmp_link;
>> -};
>> -
>> -/* Order-zero must be at least PAGE_SIZE */
>> -#define I915_BUDDY_MAX_ORDER (63 - PAGE_SHIFT)
>> -
>> -/*
>> - * Binary Buddy System.
>> - *
>> - * Locking should be handled by the user, a simple mutex around
>> - * i915_buddy_alloc* and i915_buddy_free* should suffice.
>> - */
>> -struct i915_buddy_mm {
>> -	/* Maintain a free list for each order. */
>> -	struct list_head *free_list;
>> -
>> -	/*
>> -	 * Maintain explicit binary tree(s) to track the allocation of the
>> -	 * address space. This gives us a simple way of finding a buddy block
>> -	 * and performing the potentially recursive merge step when freeing a
>> -	 * block.  Nodes are either allocated or free, in which case they will
>> -	 * also exist on the respective free list.
>> -	 */
>> -	struct i915_buddy_block **roots;
>> -
>> -	/*
>> -	 * Anything from here is public, and remains static for the lifetime of
>> -	 * the mm. Everything above is considered do-not-touch.
>> -	 */
>> -	unsigned int n_roots;
>> -	unsigned int max_order;
>> -
>> -	/* Must be at least PAGE_SIZE */
>> -	u64 chunk_size;
>> -	u64 size;
>> -	u64 avail;
>> -};
>> -
>> -static inline u64
>> -i915_buddy_block_offset(struct i915_buddy_block *block)
>> -{
>> -	return block->header & I915_BUDDY_HEADER_OFFSET;
>> -}
>> -
>> -static inline unsigned int
>> -i915_buddy_block_order(struct i915_buddy_block *block)
>> -{
>> -	return block->header & I915_BUDDY_HEADER_ORDER;
>> -}
>> -
>> -static inline unsigned int
>> -i915_buddy_block_state(struct i915_buddy_block *block)
>> -{
>> -	return block->header & I915_BUDDY_HEADER_STATE;
>> -}
>> -
>> -static inline bool
>> -i915_buddy_block_is_allocated(struct i915_buddy_block *block)
>> -{
>> -	return i915_buddy_block_state(block) == I915_BUDDY_ALLOCATED;
>> -}
>> -
>> -static inline bool
>> -i915_buddy_block_is_free(struct i915_buddy_block *block)
>> -{
>> -	return i915_buddy_block_state(block) == I915_BUDDY_FREE;
>> -}
>> -
>> -static inline bool
>> -i915_buddy_block_is_split(struct i915_buddy_block *block)
>> -{
>> -	return i915_buddy_block_state(block) == I915_BUDDY_SPLIT;
>> -}
>> -
>> -static inline u64
>> -i915_buddy_block_size(struct i915_buddy_mm *mm,
>> -		      struct i915_buddy_block *block)
>> -{
>> -	return mm->chunk_size << i915_buddy_block_order(block);
>> -}
>> -
>> -int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size);
>> -
>> -void i915_buddy_fini(struct i915_buddy_mm *mm);
>> -
>> -struct i915_buddy_block *
>> -i915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order);
>> -
>> -int i915_buddy_alloc_range(struct i915_buddy_mm *mm,
>> -			   struct list_head *blocks,
>> -			   u64 start, u64 size);
>> -
>> -void i915_buddy_free(struct i915_buddy_mm *mm, struct i915_buddy_block *block);
>> -
>> -void i915_buddy_free_list(struct i915_buddy_mm *mm, struct list_head *objects);
>> -
>> -void i915_buddy_print(struct i915_buddy_mm *mm, struct drm_printer *p);
>> -void i915_buddy_block_print(struct i915_buddy_mm *mm,
>> -			    struct i915_buddy_block *block,
>> -			    struct drm_printer *p);
>> -
>> -void i915_buddy_module_exit(void);
>> -int i915_buddy_module_init(void);
>> -
>> -#endif
>> diff --git a/drivers/gpu/drm/i915/i915_module.c b/drivers/gpu/drm/i915/i915_module.c
>> index c7507266aa83..355681dbd7a8 100644
>> --- a/drivers/gpu/drm/i915/i915_module.c
>> +++ b/drivers/gpu/drm/i915/i915_module.c
>> @@ -9,7 +9,6 @@
>>   #include "gem/i915_gem_context.h"
>>   #include "gem/i915_gem_object.h"
>>   #include "i915_active.h"
>> -#include "i915_buddy.h"
>>   #include "i915_params.h"
>>   #include "i915_pci.h"
>>   #include "i915_perf.h"
>> @@ -50,8 +49,6 @@ static const struct {
>>   	{ .init = i915_check_nomodeset },
>>   	{ .init = i915_active_module_init,
>>   	  .exit = i915_active_module_exit },
>> -	{ .init = i915_buddy_module_init,
>> -	  .exit = i915_buddy_module_exit },
>>   	{ .init = i915_context_module_init,
>>   	  .exit = i915_context_module_exit },
>>   	{ .init = i915_gem_context_module_init,
>> diff --git a/drivers/gpu/drm/i915/i915_scatterlist.c b/drivers/gpu/drm/i915/i915_scatterlist.c
>> index 4a6712dca838..84d622aa32d2 100644
>> --- a/drivers/gpu/drm/i915/i915_scatterlist.c
>> +++ b/drivers/gpu/drm/i915/i915_scatterlist.c
>> @@ -5,10 +5,9 @@
>>    */
>>   
>>   #include "i915_scatterlist.h"
>> -
>> -#include "i915_buddy.h"
>>   #include "i915_ttm_buddy_manager.h"
>>   
>> +#include <drm/drm_buddy.h>
>>   #include <drm/drm_mm.h>
>>   
>>   #include <linux/slab.h>
>> @@ -126,9 +125,9 @@ struct sg_table *i915_sg_from_buddy_resource(struct ttm_resource *res,
>>   	struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
>>   	const u64 size = res->num_pages << PAGE_SHIFT;
>>   	const u64 max_segment = rounddown(UINT_MAX, PAGE_SIZE);
>> -	struct i915_buddy_mm *mm = bman_res->mm;
>> +	struct drm_buddy_mm *mm = bman_res->mm;
>>   	struct list_head *blocks = &bman_res->blocks;
>> -	struct i915_buddy_block *block;
>> +	struct drm_buddy_block *block;
>>   	struct scatterlist *sg;
>>   	struct sg_table *st;
>>   	resource_size_t prev_end;
>> @@ -151,8 +150,8 @@ struct sg_table *i915_sg_from_buddy_resource(struct ttm_resource *res,
>>   	list_for_each_entry(block, blocks, link) {
>>   		u64 block_size, offset;
>>   
>> -		block_size = min_t(u64, size, i915_buddy_block_size(mm, block));
>> -		offset = i915_buddy_block_offset(block);
>> +		block_size = min_t(u64, size, drm_buddy_block_size(mm, block));
>> +		offset = drm_buddy_block_offset(block);
>>   
>>   		while (block_size) {
>>   			u64 len;
>> diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
>> index d59fbb019032..c4b70cb8c248 100644
>> --- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
>> +++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
>> @@ -8,14 +8,15 @@
>>   #include <drm/ttm/ttm_bo_driver.h>
>>   #include <drm/ttm/ttm_placement.h>
>>   
>> +#include <drm/drm_buddy.h>
>> +
>>   #include "i915_ttm_buddy_manager.h"
>>   
>> -#include "i915_buddy.h"
>>   #include "i915_gem.h"
>>   
>>   struct i915_ttm_buddy_manager {
>>   	struct ttm_resource_manager manager;
>> -	struct i915_buddy_mm mm;
>> +	struct drm_buddy_mm mm;
>>   	struct list_head reserved;
>>   	struct mutex lock;
>>   	u64 default_page_size;
>> @@ -34,7 +35,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
>>   {
>>   	struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
>>   	struct i915_ttm_buddy_resource *bman_res;
>> -	struct i915_buddy_mm *mm = &bman->mm;
>> +	struct drm_buddy_mm *mm = &bman->mm;
>>   	unsigned long n_pages;
>>   	unsigned int min_order;
>>   	u64 min_page_size;
>> @@ -73,7 +74,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
>>   	n_pages = size >> ilog2(mm->chunk_size);
>>   
>>   	do {
>> -		struct i915_buddy_block *block;
>> +		struct drm_buddy_block *block;
>>   		unsigned int order;
>>   
>>   		order = fls(n_pages) - 1;
>> @@ -82,7 +83,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
>>   
>>   		do {
>>   			mutex_lock(&bman->lock);
>> -			block = i915_buddy_alloc(mm, order);
>> +			block = drm_buddy_alloc(mm, order);
>>   			mutex_unlock(&bman->lock);
>>   			if (!IS_ERR(block))
>>   				break;
>> @@ -106,7 +107,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
>>   
>>   err_free_blocks:
>>   	mutex_lock(&bman->lock);
>> -	i915_buddy_free_list(mm, &bman_res->blocks);
>> +	drm_buddy_free_list(mm, &bman_res->blocks);
>>   	mutex_unlock(&bman->lock);
>>   err_free_res:
>>   	kfree(bman_res);
>> @@ -120,7 +121,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);
>> -	i915_buddy_free_list(&bman->mm, &bman_res->blocks);
>> +	drm_buddy_free_list(&bman->mm, &bman_res->blocks);
>>   	mutex_unlock(&bman->lock);
>>   
>>   	kfree(bman_res);
>> @@ -130,17 +131,17 @@ static void i915_ttm_buddy_man_debug(struct ttm_resource_manager *man,
>>   				     struct drm_printer *printer)
>>   {
>>   	struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
>> -	struct i915_buddy_block *block;
>> +	struct drm_buddy_block *block;
>>   
>>   	mutex_lock(&bman->lock);
>>   	drm_printf(printer, "default_page_size: %lluKiB\n",
>>   		   bman->default_page_size >> 10);
>>   
>> -	i915_buddy_print(&bman->mm, printer);
>> +	drm_buddy_print(&bman->mm, printer);
>>   
>>   	drm_printf(printer, "reserved:\n");
>>   	list_for_each_entry(block, &bman->reserved, link)
>> -		i915_buddy_block_print(&bman->mm, block, printer);
>> +		drm_buddy_block_print(&bman->mm, block, printer);
>>   	mutex_unlock(&bman->lock);
>>   }
>>   
>> @@ -190,7 +191,7 @@ int i915_ttm_buddy_man_init(struct ttm_device *bdev,
>>   	if (!bman)
>>   		return -ENOMEM;
>>   
>> -	err = i915_buddy_init(&bman->mm, size, chunk_size);
>> +	err = drm_buddy_init(&bman->mm, size, chunk_size);
>>   	if (err)
>>   		goto err_free_bman;
>>   
>> @@ -228,7 +229,7 @@ int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
>>   {
>>   	struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
>>   	struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
>> -	struct i915_buddy_mm *mm = &bman->mm;
>> +	struct drm_buddy_mm *mm = &bman->mm;
>>   	int ret;
>>   
>>   	ttm_resource_manager_set_used(man, false);
>> @@ -240,8 +241,8 @@ int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
>>   	ttm_set_driver_manager(bdev, type, NULL);
>>   
>>   	mutex_lock(&bman->lock);
>> -	i915_buddy_free_list(mm, &bman->reserved);
>> -	i915_buddy_fini(mm);
>> +	drm_buddy_free_list(mm, &bman->reserved);
>> +	drm_buddy_fini(mm);
>>   	mutex_unlock(&bman->lock);
>>   
>>   	ttm_resource_manager_cleanup(man);
>> @@ -264,11 +265,11 @@ int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man,
>>   			       u64 start, u64 size)
>>   {
>>   	struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
>> -	struct i915_buddy_mm *mm = &bman->mm;
>> +	struct drm_buddy_mm *mm = &bman->mm;
>>   	int ret;
>>   
>>   	mutex_lock(&bman->lock);
>> -	ret = i915_buddy_alloc_range(mm, &bman->reserved, start, size);
>> +	ret = drm_buddy_alloc_range(mm, &bman->reserved, start, size);
>>   	mutex_unlock(&bman->lock);
>>   
>>   	return ret;
>> diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.h b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.h
>> index 0722d33f3e14..fa644b512c2e 100644
>> --- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.h
>> +++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.h
>> @@ -13,7 +13,7 @@
>>   
>>   struct ttm_device;
>>   struct ttm_resource_manager;
>> -struct i915_buddy_mm;
>> +struct drm_buddy_mm;
>>   
>>   /**
>>    * struct i915_ttm_buddy_resource
>> @@ -28,7 +28,7 @@ struct i915_buddy_mm;
>>   struct i915_ttm_buddy_resource {
>>   	struct ttm_resource base;
>>   	struct list_head blocks;
>> -	struct i915_buddy_mm *mm;
>> +	struct drm_buddy_mm *mm;
>>   };
>>   
>>   /**
>> diff --git a/include/drm/drm_buddy.h b/include/drm/drm_buddy.h
>> new file mode 100644
>> index 000000000000..5ce3fc702f80
>> --- /dev/null
>> +++ b/include/drm/drm_buddy.h
>> @@ -0,0 +1,153 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2021 Intel Corporation
>> + */
>> +
>> +#ifndef __DRM_BUDDY_H__
>> +#define __DRM_BUDDY_H__
>> +
>> +#include <linux/bitops.h>
>> +#include <linux/list.h>
>> +#include <linux/slab.h>
>> +#include <linux/sched.h>
>> +
>> +#include <drm/drm_print.h>
>> +
>> +#define range_overflows(start, size, max) ({ \
>> +	typeof(start) start__ = (start); \
>> +	typeof(size) size__ = (size); \
>> +	typeof(max) max__ = (max); \
>> +	(void)(&start__ == &size__); \
>> +	(void)(&start__ == &max__); \
>> +	start__ >= max__ || size__ > max__ - start__; \
>> +})
>> +
>> +struct drm_buddy_block {
>> +#define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
>> +#define DRM_BUDDY_HEADER_STATE  GENMASK_ULL(11, 10)
>> +#define   DRM_BUDDY_ALLOCATED	   (1 << 10)
>> +#define   DRM_BUDDY_FREE	   (2 << 10)
>> +#define   DRM_BUDDY_SPLIT	   (3 << 10)
>> +/* Free to be used, if needed in the future */
>> +#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(9, 6)
>> +#define DRM_BUDDY_HEADER_ORDER  GENMASK_ULL(5, 0)
>> +	u64 header;
>> +
>> +	struct drm_buddy_block *left;
>> +	struct drm_buddy_block *right;
>> +	struct drm_buddy_block *parent;
>> +
>> +	void *private; /* owned by creator */
>> +
>> +	/*
>> +	 * While the block is allocated by the user through drm_buddy_alloc*,
>> +	 * the user has ownership of the link, for example to maintain within
>> +	 * a list, if so desired. As soon as the block is freed with
>> +	 * drm_buddy_free* ownership is given back to the mm.
>> +	 */
>> +	struct list_head link;
>> +	struct list_head tmp_link;
>> +};
>> +
>> +/* Order-zero must be at least PAGE_SIZE */
>> +#define DRM_BUDDY_MAX_ORDER (63 - PAGE_SHIFT)
>> +
>> +/*
>> + * Binary Buddy System.
>> + *
>> + * Locking should be handled by the user, a simple mutex around
>> + * drm_buddy_alloc* and drm_buddy_free* should suffice.
>> + */
>> +struct drm_buddy_mm {
>> +	/* Maintain a free list for each order. */
>> +	struct list_head *free_list;
>> +
>> +	/*
>> +	 * Maintain explicit binary tree(s) to track the allocation of the
>> +	 * address space. This gives us a simple way of finding a buddy block
>> +	 * and performing the potentially recursive merge step when freeing a
>> +	 * block.  Nodes are either allocated or free, in which case they will
>> +	 * also exist on the respective free list.
>> +	 */
>> +	struct drm_buddy_block **roots;
>> +
>> +	/*
>> +	 * Anything from here is public, and remains static for the lifetime of
>> +	 * the mm. Everything above is considered do-not-touch.
>> +	 */
>> +	unsigned int n_roots;
>> +	unsigned int max_order;
>> +
>> +	/* Must be at least PAGE_SIZE */
>> +	u64 chunk_size;
>> +	u64 size;
>> +	u64 avail;
>> +};
>> +
>> +static inline u64
>> +drm_buddy_block_offset(struct drm_buddy_block *block)
>> +{
>> +	return block->header & DRM_BUDDY_HEADER_OFFSET;
>> +}
>> +
>> +static inline unsigned int
>> +drm_buddy_block_order(struct drm_buddy_block *block)
>> +{
>> +	return block->header & DRM_BUDDY_HEADER_ORDER;
>> +}
>> +
>> +static inline unsigned int
>> +drm_buddy_block_state(struct drm_buddy_block *block)
>> +{
>> +	return block->header & DRM_BUDDY_HEADER_STATE;
>> +}
>> +
>> +static inline bool
>> +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_free(struct drm_buddy_block *block)
>> +{
>> +	return drm_buddy_block_state(block) == DRM_BUDDY_FREE;
>> +}
>> +
>> +static inline bool
>> +drm_buddy_block_is_split(struct drm_buddy_block *block)
>> +{
>> +	return drm_buddy_block_state(block) == DRM_BUDDY_SPLIT;
>> +}
>> +
>> +static inline u64
>> +drm_buddy_block_size(struct drm_buddy_mm *mm,
>> +		     struct drm_buddy_block *block)
>> +{
>> +	return mm->chunk_size << drm_buddy_block_order(block);
>> +}
>> +
>> +int drm_buddy_init(struct drm_buddy_mm *mm, u64 size, u64 chunk_size);
>> +
>> +void drm_buddy_fini(struct drm_buddy_mm *mm);
>> +
>> +struct drm_buddy_block *
>> +drm_buddy_alloc(struct drm_buddy_mm *mm, unsigned int order);
>> +
>> +int drm_buddy_alloc_range(struct drm_buddy_mm *mm,
>> +			  struct list_head *blocks,
>> +			  u64 start, u64 size);
>> +
>> +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);
>> +
>> +void drm_buddy_print(struct drm_buddy_mm *mm, struct drm_printer *p);
>> +void drm_buddy_block_print(struct drm_buddy_mm *mm,
>> +			   struct drm_buddy_block *block,
>> +			   struct drm_printer *p);
>> +
>> +void drm_buddy_module_exit(void);
>> +int drm_buddy_module_init(void);
>> +
>> +#endif
> 


More information about the amd-gfx mailing list